DEV Community

Aaron K Saunders
Aaron K Saunders

Posted on

2 1 1 1 1

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

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (1)

Collapse
 
dansasser profile image
Daniel T Sasser II

Saw your YouTube video and hopped over here. Good stuff! This new release of PayloadCMS really made some changes. They definitely went all in with NextJS. I really like PayloadCMS for the quick setup while working on our custom CMS. I'll follow her on dev and YouTube. Feel free to click on my profile. Most of my stuff is research but I'm working on a few tutorials and reviews.

Cloudinary image

Video API: manage, encode, and optimize for any device, channel or network condition. Deliver branded video experiences in minutes and get deep engagement insights.

Learn more

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay