DEV Community

Aaron K Saunders
Aaron K Saunders

Posted on

Payload CMS: Add A Custom Create Account Screen In Admin UI

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
Enter fullscreen mode Exit fullscreen mode

Image description

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;
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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('')
Enter fullscreen mode Exit fullscreen mode

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)
    }
  }
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

Completed Form UI

Image description

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
  ],
})
Enter fullscreen mode Exit fullscreen mode

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
  ],
}
Enter fullscreen mode Exit fullscreen mode

Links

Payload CMS Root Views
Project Source Code

Top comments (0)