I try to create my first custom field.
The doc example
How to import React.FC if React deps are not installed
You should be able to add react at the top
import React from 'react'
Is that not the case?
im using pnpm
Ahhh, then you may need to install React yourself
pnpm works a bit differently out of the box compared to yarn
ok then the same version: ^18.2.0 I see
There is no UI Kit right? or there is maybe a way to get the same css?
Here's some links:
There's no UI kit in the sense that you can see what components are available to you easily,
yet
A storybook implementation is in the works that would let you see what you have available, however you
canreuse components
but you need to check out existing code from core and plugins to figure out their paths and what's available 😬
ok ok
If you want a simple component styling, the easiest way is to look into the css variables globally available
last question, the example code has a problem no?
So you can at least write your own css using the variables thats gonna make it compatible with dark/light mode and re-use similar colours and vars
my screen stay blank , but no error in console
Yes...The code itself I think is correct but the types are wrong
Here's an example component of mine building a number field with the react module
import React from 'react'
import { useField } from 'payload/components/forms'
import { NumericFormat } from 'react-number-format'
import { TextInput } from 'payload/components/forms'
import { Label } from 'payload/components/forms'
import { RowAdmin } from 'payload/dist/fields/config/types'
import { transformPriceString, reversePriceToString } from '../utilities/transformPriceString'
type Props = {
path: string
showError?: boolean
readOnly?: boolean
className?: string
required?: boolean
placeholder?: Record<string, string> | string
style?: React.CSSProperties
width?: string
label?: string
admin: RowAdmin
}
const PriceInput: React.FC<Props> = ({ showError, readOnly, className, required, path, placeholder, label, admin, ...others }) => {
const { value, setValue } = useField<number>({ path })
const [visualValue, setVisualValue] = React.useState<number | string>(reversePriceToString(value))
const { style, width } = admin
const classes = ['field-type', 'text', className, showError && 'error', readOnly && 'read-only'].filter(Boolean).join(' ')
return (
<div
className={classes}
style={{
...style,
width,
}}>
<Label htmlFor={`field-${path.replace(/\./gi, '__')}`} label={label} required={required} />
<NumericFormat
onChange={(e) => {
const transfomedPrice = transformPriceString(e.target.value)
setVisualValue(e.target.value)
setValue(transfomedPrice)
}}
value={visualValue}
id={`field-${path.replace(/\./gi, '__')}`}
name={path}
fixedDecimalScale
required={required}
thousandSeparator=','
placeholder={typeof placeholder === 'string' ? placeholder : '$24,000.00'}
prefix='$'
decimalScale={2}
/>
</div>
)
}
export default PriceInput
Ok thx, I will try with your example 👍
The TextInput from there is actually unused, I think I had an issue with props or ref forwarding
ok ok, anyway I will start with a simple field
I don't understand, what should I set in 'path', it's the component name?
custom components are passed down a "path" property, it is used as a key to store the value in form state. TLDR; it is provided for you.
Everything in the props above there is passed down into your component, some of them are things from the field configuration
oh ok I see 🙂
Am I doing something wrong? Path seems to be undefined
My component
The way I call it :
then the value is never saved in DB
I think it's cause you're using the
ui
field
It's more of a "insert your react components here" field than a data field to my knowledge
So use a
text
or a
number
field for example if you wanna store a particular type of data but override the frontend component of how it looks or functions
I use the UI field for buttons that trigger other behaviours for example, or for pulling in visual information. ^ my example I posted above is attached to a number field
I haven't tried the
ui
field for data at all tbh 🤔
ohh ok, I thought "ui" was the only field that could receive admin > components > Field
better, but nothing happens when I save it 😬
it's ok, I just add the label, and the input type 🤷♂️
nope, any field can use custom components 🙂
You are likely looking to use a
text
field with a custom component, so you can save the data from your custom component?
Yep, its working finally 😁
I just copy the same structure than the others inputs (name, htmlFor, classname ...), and now looks like a official one :
When in doubt you can just go into the core code of payload and see how all the different types of fields are constructed
Ill send some links a bit later when im at my laptop
Yep, I think there is an util function to transform the input name to get the Title (with word spacing and uppercase)
Most stuff from core can be imported even if your ide doesnt autosuggest
Just use the more absolute path
I did not see a way to add custom behavior to "readOnly". I can only set a boolean, we don't have access to data?
pretty nice
What do you mean?
Youre getting a boolean readOnly...you can choose what you actually do with it
On fields you can also use custom for anything unique to your fields config
you will need to import and use some of our exposed hooks:
https://payloadcms.com/docs/admin/hookscustom
on the field configuration
I try to reproduce my Sanity template, I lastely I was able to set readonly, depending of other fields
📵
ill check back in here in a couple of hours
that's really nice, thank you 🙏
in a custom component, you will need to access the form data with the hooks I linked above
yep, but in a current payload field, is there a way? In my Sanity template, I created a "link" field.
readOnly: is set to true if the "Page" field is already set.
No, it is currently just a boolean. But it would be pretty simple to create a custom component like so:
import React from 'react'
import { Text, useField } from 'payload/components/forms'
import { Props } from 'payload/dist/admin/components/forms/field-types/Text/types'
export const ConditionalField: React.FC<Props> = incomingProps => {
const { value: watchValue } = useField({
path: 'field-to-watch',
})
const readOnly = !watchValue ? false : true
const props: Props = {
...incomingProps,
admin: {
...incomingProps.admin,
readOnly,
},
}
return <Text {...props} />
}
or if you don't want to create a new object, you could mutate the incomingProps with
incomingProps.admin.readOnly = !watchValue ? false : true
and spread that into your <Text {...incomingProps /> component
Thx so much 🙏
Nice, you got it handled!
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.