Skip to content

Production Guides & Recipes

The slugField factory generates URL-friendly slugs while preserving non-Latin scripts (such as Persian and Arabic):

import { slugField } from "@nexload-sdk/payload-fields";
const fields = [
{ name: "title", type: "text", required: true },
...slugField({
source: "title",
overrides: {
admin: {
position: "sidebar",
description: "Auto-generated from title. Unlock to manually edit.",
},
},
}),
];
  • When a document is first created, slugLock defaults to true.
  • Editing the title field automatically updates slug.
  • Once the user clicks the lock icon to lock the slug, future changes to title will never alter slug.
  • This prevents unintentional breakage of indexed search engine URLs.

Store standard ISO datetimes while providing a rich Persian Solar calendar widget in Payload Admin:

import { jalaliDateField } from "@nexload-sdk/payload-fields";
const fields = [
jalaliDateField({
name: "eventDate",
pickerAppearance: "dayAndTime", // Options: "dayOnly" | "dayAndTime"
overrides: {
required: true,
admin: {
description: "Select date in Iranian Solar (Shamsi) calendar.",
},
},
}),
];
  • The database always stores standard UTC ISO 8601 strings (e.g. 2026-07-24T12:00:00.000Z).
  • Date ranges, GraphQL queries, and database sorting (sort: "-eventDate") operate natively without custom database adapters.

Prevent JavaScript floating point inaccuracies (e.g. 0.1 + 0.2 === 0.30000000000000004) by storing currency in integer minor units:

import { moneyField } from "@nexload-sdk/payload-fields";
const fields = [
moneyField({
name: "unitPrice",
currency: "USD", // Stored in cents (e.g. $19.99 is stored as 1999)
minMinorUnits: 0,
maxMinorUnits: 10_000_000,
overrides: {
required: true,
},
}),
moneyField({
name: "rialPrice",
currency: "IRR", // Stored in Rials
minMinorUnits: 0,
}),
];
  • Payload Admin UI: The user types human-readable amounts (e.g. 19.99). The custom Admin component formats the input with thousands separators.
  • REST / GraphQL / Local API: Callers must always send and receive the raw integer minor unit (1999).

When slug generation requires asynchronous database checks or business rules:

import { payloadFieldsPlugin, slugField } from "@nexload-sdk/payload-fields";
import { buildConfig } from "payload";
export default buildConfig({
collections: [
{
slug: "posts",
fields: [
{ name: "title", type: "text", required: true },
...slugField({ source: "title", generator: "postSlug" }),
],
},
],
plugins: [
payloadFieldsPlugin({
// Protect the generation endpoint
generateSlugAccess: ({ req }) => Boolean(req.user),
slugGenerators: {
postSlug: async ({ sourceValue, req }) => {
const baseSlug = sourceValue.trim().toLowerCase().replace(/\s+/g, "-");
// Check for existing slug collision
const count = await req.payload.count({
collection: "posts",
where: { slug: { equals: baseSlug } },
});
return count.totalDocs > 0 ? `${baseSlug}-${Date.now()}` : baseSlug;
},
},
}),
],
});

1. Custom Admin Controls Do Not Render (Fallback to Standard Inputs)

Section titled “1. Custom Admin Controls Do Not Render (Fallback to Standard Inputs)”

If Payload Admin displays plain number or text inputs instead of the Jalali datepicker or formatted money input:

  • Regenerate Payload’s Import Map by running your Payload build command:
    Terminal window
    pnpm payload generate:importmap
  • Verify that React and React-DOM are version ^19.0.0 to match Payload 3.

2. Money Field Validation Failure on Local API Write

Section titled “2. Money Field Validation Failure on Local API Write”

If payload.create({ data: { price: 19.99 } }) fails validation: Remember that the database field accepts only integers. Pass 1999 (in minor units) instead of 19.99.

The generator endpoint requires an authenticated user session by default. Ensure the browser request includes credentials (credentials: "include") or pass an explicit generateSlugAccess handler in payloadFieldsPlugin.