Hi everybody, I am new to payload and I am trying to create a custom form field for a phone number. I am using the website template and I am trying to create a custom form field within the form builder, if that is possible. I have gotten stuck.
I have:
created a component based of the textfield type with validation, similar to the default form fields
added it to the fields file that exports it to the plugins and then into payload-config
tried adding it to the form builder plugin as true, which didn't work and have also tested variations of formoverrides in the formbuilderplugin code. Has anyone solved this?
thanks, sorry for any newbie things in this post 🙂
The form file is inspired by the existing forms
export const Phone: React.FC<
TextField & {
errors: Partial<FieldErrorsImpl>
register: UseFormRegister<FieldValues>
}
= ({ name, defaultValue, errors, label, register, required, width }) => { return ( <width...> etc)}
imported it into my fields.ts file:
import { Phone } from './Phone'
export const fields = {
phone: Phone,
// ... existing code
}
And tested a bunch of stuff in the plugins file (and tried to generate to payload-types file):
formBuilderPlugin({
fields: {
payment: false,
phone: true,
},
part from the above I have tried a few variations of the formoverides and the submissionoverrides, but havent managed to get it to work, found here: https://payloadcms.com/docs/plugins/form-builder
>
I'm dealing with the exact same issue as we speak, have created additional form blocks for phone number, and mobile phone number, can't seem to work out how to pass them to the form builder plug in config correctly. Followed the same approach as you have. Going to keep working on it today, will update here when I work it out. 🙂
I solved this. I added a path in the form blocks directory for phoneNumber, in this directory i have a block.ts file, and an index.tsx file.
The block.ts file initiates the component in the admin panel, I had to create the required fields here, and then pass these fields in the block object structure. If you use the Field type from payload for each Field, and then the Block type from payload for your Block component, you shouldn't need to create any extended types etc in your client component that renders the block.
This is my index.ts file here which looks after the tsx, and renders our PhoneNumber block. I have some additional validation here, but you can configure this to your specific requirements (mines localised for Australian mobile / cell numbers currently).
Then in my form builder plug in config, I simply just pass in the newly created form field into the fields object.
I hope this works for you.
import { Field, Block } from "payload";
export const name: Field = {
name: "name",
type: "text",
label: "Name (lowercase, no special characters please)",
required: true,
};
export const label: Field = {
name: "label",
type: "text",
label: "Label",
localized: true,
};
export const required: Field = {
name: "required",
type: "checkbox",
label: "Required",
defaultValue: true,
};
export const width: Field = {
name: "width",
type: "number",
label: "Field width (percentage)",
};
export const PhoneNumber: Block = {
slug: "phoneNumber",
fields: [
{
type: "row",
fields: [
{
...name,
admin: {
width: "50%",
},
},
{
...label,
admin: {
width: "50%",
},
},
],
},
{
type: "row",
fields: [
{
...width,
admin: {
width: "50%",
},
},
{
name: "defaultValue",
type: "text",
label: "Default Value",
admin: {
width: "50%",
},
},
],
},
required,
],
labels: {
singular: "Phone Number",
plural: "Phone Numbers",
},
};import React from "react";
import type { FieldErrorsImpl, FieldValues, UseFormRegister } from "react-hook-form";
import type { TextField } from "@payloadcms/plugin-form-builder/types";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Error } from "../Error";
import { Width } from "../Width";
export const PhoneNumber: React.FC<
TextField & {
placeholder?: string;
errors: Partial<FieldErrorsImpl>;
register: UseFormRegister<FieldValues>;
}
> = ({ name, defaultValue, errors, label, register, required, width, placeholder }) => {
return (
<Width width={width}>
<Label className="block mb-1" htmlFor={name}>
{label}
{required && (
<span className="required">
* <span className="sr-only">(required)</span>
</span>
)}
</Label>
<Input
defaultValue={defaultValue}
placeholder={placeholder || "04XX XXX XXX or +61 X XXXX XXXX"}
id={name}
type="tel"
{...register(name, {
required,
validate: (value) => {
if (!value && !required) return true;
const digitsOnly = value.replace(/\D/g, "");
const isAusMobile = /^04\d{8}$/.test(digitsOnly);
const isIntMobile = /^\+61\d{9}$/.test(digitsOnly);
return isAusMobile || isIntMobile || "Please enter a valid mobile number";
},
})}
/>
{errors[name] && <Error name={name} />}
</Width>
);
};import { formBuilderPlugin } from "@payloadcms/plugin-form-builder";
import { FixedToolbarFeature, HeadingFeature, lexicalEditor } from "@payloadcms/richtext-lexical";
import { PhoneNumber } from "@/blocks/Form/PhoneNumber/block";
export const formBuilderConfig = formBuilderPlugin({
fields: {
payment: false,
phoneNumber: PhoneNumber,
},
formOverrides: {
fields: ({ defaultFields }) => {
return defaultFields.map((field) => {
if ("name" in field && field.name === "confirmationMessage") {
return {
...field,
editor: lexicalEditor({
features: ({ rootFeatures }) => {
return [
...rootFeatures,
FixedToolbarFeature(),
HeadingFeature({ enabledHeadingSizes: ["h1", "h2", "h3", "h4"] }),
];
},
}),
};
}
return field;
});
},
},
});hey, great work, didn't come back to this post until today, had given up hope it would get a response. I'll see what I can learn from what you posted above and let you know. Appreciate it a lot, thanks.
Hey
@476408822538305538, no problem. It is definitely not overly clear in the docs etc. It would be nice if they included the admin panel block files in the website example, it would make it a bit clearer. I am not sure my approach is perfect, But seems to work well and function as expected in the app i'm working on. Good luck with your project.
hey
@702287438206468200it worked, thanks. And it looks good too. Becoming a programmer feels daunting....good luck with your project too.
Hey @Anders. Haha don't worry my friend, I know that feeling, the daunting feeling never goes away, I just learned to embrace it. Really happy you got it working ok. Feel free to mention me if you have any future issues, Not sure I'll always be able to help, but I'm always happy to take a look.
thanks
@702287438206468200, appreciate it.
thank you
@702287438206468200that helped me too!
here is also a good post that adds variation to tomkats solution:
https://dev.to/mikecebul/payload-from-builder-plugin-custom-fields-5d3jStar
Discord
online
Get dedicated engineering support directly from the Payload team.