Form
GitHubForm wrapper for next-safe-action that uploads tracked files through a get-upload-url API route before submitting FormData.
Installation
pnpm dlx shadcn@latest add @cs/formUsage
import {
Form,
} from "@/components/cs/form";
import { FormSubmit } from "@/components/cs/form-submit";
import { FormCheckbox } from "@/components/cs/form-checkbox";
import { FormDateInput } from "@/components/cs/form-date-input";
import { FormError } from "@/components/cs/form-error";
import { FormHidden } from "@/components/cs/form-hidden";
import { FormInput } from "@/components/cs/form-input";
import { FormOtpInput } from "@/components/cs/form-otp-input";
import { FormPasswordInput } from "@/components/cs/form-password-input";
import {
FormRadioGroup,
FormRadioGroupItem,
} from "@/components/cs/form-radio-group";
import { FormSelect } from "@/components/cs/form-select";
import { FormSwitch } from "@/components/cs/form-switch";<Form action={saveProfile} uploadUrlApiPath="/api/file/upload-url">
<FormInput name="name" label="Name" />
<FormSubmit label="Save changes" />
</Form>Verification code
Automatically submits a six-digit verification code and shows its value in a toast. Trying to submit a code other than "123456" will result in an error message.
"use client"
import { useRef } from "react"
import { Form } from "@/registry/cs/ui/form"
import { FormOtpInput } from "@/registry/cs/ui/form-otp-input"
import { actionResult } from "@/registry/cs/lib/action-result"
export default function FormAutoSubmitOtpPreview() {
const codeRef = useRef("")
return (
<Form
action={async () => ({
data:
codeRef.current == "123456"
? actionResult(200, "Code accepted")
: actionResult(400, "Invalid code"),
})}
autosave
withoutRedirectBack
>
<FormOtpInput
name="code"
maxLength={6}
onChange={(value) => {
codeRef.current = value
}}
/>
</Form>
)
}
API Reference
DefaultFile
File metadata rendered as an attachment already stored by the application.
type DefaultFile = {
/** Stable file identifier used as the rendered item key. */
uuid: string
/** File name displayed to the user. */
name: string
/** MIME type used to choose the attachment preview. */
type: string
/** Public or signed URL used to load the file preview. */
src: string
}CsFile
A file selected in the form and tracked through its upload lifecycle.
type CsFile = { file: File; hidden: boolean; state: "idle" | "error" | "done" | "uploading" | "processing" }useFormContext
Returns the state and operations provided by the nearest Form.
Form
Wraps a next-safe-action form with pending state, validation results, file tracking, toast feedback, redirects, and optional autosave support. Files added through useFormContext().pushFile are uploaded before the server action runs. Configure uploadUrlApiPath with a GET route backed by getS3FileUploadUrl; the completed file UUIDs are appended to FormData as files entries.
Props
| Name | Type | Default | Description |
|---|---|---|---|
| action * | FormAction<TData> | - | Server action with a Standard Schema input that receives the submitted FormData. |
| autosave | boolean | false | Submits the form when submitAutosave is called. |
| children * | React.ReactNode | - | Form fields and controls. |
| className | string | - | Additional classes applied to the field group. |
| initResult | FormActionInitialResult<TData> | - | Initial next-safe-action result used before the first submission. |
| onSuccessCallback | (() => void) | undefined | - | Called after a successful action result is received. |
| uploadUrlApiPath | string | "/api/file/upload-url" | Prepares a direct file upload. The default route is /api/file/upload-url. |
| withoutRedirectBack | boolean | false | Disables navigation back when a successful action has no redirect. |