Payload CMS
Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.
Overview
This is a sample of my approach for extending the Admin Console to allow a user to create an account in the system.
We will add a new route to the admin console that presents a form for creating a user.
This is just the basic integration and not sufficient for a production environment. This is just me sharing as I am learning new and interesting thing to do with Payload CMS
So to get started, create a new project
npx create-payload-app
Create the Custom View Component
create a components directory and create a new file CreateAccountViewComponent
. I started by copying the code from the website on creating custom views.
import type { AdminViewProps } from 'payload'
import { DefaultTemplate } from '@payloadcms/next/templates'
import { Gutter } from '@payloadcms/ui'
import React from 'react'
export const CreateAccountView: React.FC<AdminViewProps> = ({
initPageResult,
params,
searchParams,
}) => {
return (
<DefaultTemplate
i18n={initPageResult.req.i18n}
locale={initPageResult.locale}
params={params}
payload={initPageResult.req.payload}
permissions={initPageResult.permissions}
searchParams={searchParams}
user={initPageResult.req.user || undefined}
visibleEntities={initPageResult.visibleEntities}
>
<Gutter>
<h1>Custom Default Root View</h1>
<p>This view uses the Default Template.</p>
</Gutter>
</DefaultTemplate>
)
}
export default CreateAccountView;
Create the Client Component to Submit Form
Since this is a server component, I created a new component that will be a client component to render the UI and handle the click event on the button.
The client component will be called CreateAccountForm.tsx
, update the code in the server component, to include the client component which will have the create account form.
<Gutter>
<h1>Create Account</h1>
<CreateAccountForm />
</Gutter>
The create account component was challenging because the documentation on the fields and appropriate way to import the payload object where, IMHO, difficult to follow and there are not many examples.
After digging around in the Payload CMS GitHub repo and scrolling through the source code in the node_modules
directory, this is what I came up with.
First off the imports to get some components that look like the rest of the application, the PasswordInput
was a tricky one to track down; I thought it would be with the rest of the Input elements but it wasn't.
import { useRouter } from 'next/navigation'
import { Button, TextInput } from '@payloadcms/ui'
import { PasswordInput } from 'node_modules/@payloadcms/ui/dist/fields/Password/input.js'
import React, { useState } from 'react'
The rest of the code is straight forward, we use useState
to get the fields for creating the new user, and we need the router for redirecting.
const router = useRouter()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
Then we have the handleSubmit
function for creating the new user. We will call the /api/users
route with the POST
method and pass the email and password in the body.
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
try {
const res = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
})
if (!res.ok) throw new Error('Create account failed')
router.push('/admin')
} catch (error) {
console.error('Create account error:', error)
}
}
Finally, the input form UI using the Payload UI Components to help the UI look consistent with rest of Admin Console.
return (
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
<TextInput
label="Email"
path="email"
placeholder="Email"
value={email}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}
/>
<PasswordInput
label="Password"
path="password"
value={password}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPassword(e.target.value)}
/>
<Button type="submit">Create Account</Button>
</form>
)
}
Completed Form UI
Update Payload Configuration
Now we need to modify the payload-config.ts
file so the new page can be rendered when the user goes to the route /admin/create-account
We will update the admin
section of the file to include the inclusion of the new component.
Make sure ypu use the full path when referencing where you create the
CreateAccountViewComponent
file, otherwise it will not work.
export default buildConfig({
admin: {
user: Users.slug,
importMap: {
baseDir: path.resolve(dirname),
},
// NEW STUFF BELOW
// =================
components: {
views: {
'create-account': {
Component: 'src/components/Users/CreateAccountViewComponent',
path: '/create-account',
},
},
},
// END
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: sqliteAdapter({
client: {
url: process.env.DATABASE_URI || '',
},
}),
sharp,
plugins: [
payloadCloudPlugin(),
// storage-adapter-placeholder
],
})
Final thing to do is to update the User
collection to allow anyone to create user's otherwise the the api call will fail.
I know this is not secure or a best practice, I am just creating this content to demonstrate functionality of Payload CMS
So in your User collection...
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
access: {
create: () => true, // NEW CODE ALLOWING ANYONE TO CREATE
},
fields: [
// Email added by default
// Add more fields as needed
],
}
Top comments (0)