or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

components.mdfields.mdforms.mdhooks.mdicons.mdindex.mdproviders.mdutilities.md

forms.mddocs/

0

# Form System and Utilities

1

2

Complete form management system with validation, state handling, submission, and field rendering capabilities for building dynamic forms in Payload CMS admin interfaces.

3

4

## Form Components

5

6

### Form

7

8

Main form wrapper component that provides form context and handles state management.

9

10

```typescript { .api }

11

interface FormProps {

12

onSubmit?: (data: Record<string, unknown>, actions: FormActions) => void | Promise<void>;

13

onChange?: (data: Record<string, unknown>) => void;

14

initialState?: Record<string, unknown>;

15

disabled?: boolean;

16

readOnly?: boolean;

17

validationOperation?: 'create' | 'update';

18

className?: string;

19

children?: React.ReactNode;

20

method?: 'POST' | 'PATCH';

21

action?: string;

22

encType?: string;

23

}

24

25

interface FormActions {

26

reset: () => void;

27

submit: () => Promise<void>;

28

validate: () => boolean;

29

}

30

31

function Form(props: FormProps): JSX.Element;

32

```

33

34

Usage example:

35

```typescript

36

import { Form, TextField, FormSubmit } from '@payloadcms/ui';

37

38

function DocumentForm() {

39

const handleSubmit = async (data, { reset }) => {

40

try {

41

await saveDocument(data);

42

reset();

43

} catch (error) {

44

console.error('Save failed:', error);

45

}

46

};

47

48

return (

49

<Form onSubmit={handleSubmit}>

50

<TextField path="title" label="Title" required />

51

<TextField path="content" label="Content" />

52

<FormSubmit>Save Document</FormSubmit>

53

</Form>

54

);

55

}

56

```

57

58

### FormSubmit

59

60

Form submission button component.

61

62

```typescript { .api }

63

interface FormSubmitProps {

64

children?: React.ReactNode;

65

disabled?: boolean;

66

processing?: boolean;

67

type?: 'submit' | 'button';

68

className?: string;

69

}

70

71

function FormSubmit(props: FormSubmitProps): JSX.Element;

72

```

73

74

### RenderFields

75

76

Dynamically render form fields from configuration.

77

78

```typescript { .api }

79

interface RenderFieldsProps {

80

fields: FieldConfig[];

81

path?: string;

82

margins?: boolean;

83

className?: string;

84

readOnly?: boolean;

85

permissions?: Record<string, unknown>;

86

}

87

88

function RenderFields(props: RenderFieldsProps): JSX.Element;

89

```

90

91

Usage example:

92

```typescript

93

import { RenderFields } from '@payloadcms/ui';

94

95

function DynamicForm({ fieldConfig }) {

96

return (

97

<Form>

98

<RenderFields

99

fields={fieldConfig}

100

margins={true}

101

/>

102

</Form>

103

);

104

}

105

```

106

107

### RowLabel

108

109

Label component for repeatable row fields.

110

111

```typescript { .api }

112

interface RowLabelProps {

113

data: Record<string, unknown>;

114

index: number;

115

path: string;

116

label?: string;

117

fallback?: string;

118

}

119

120

function RowLabel(props: RowLabelProps): JSX.Element;

121

```

122

123

## Form State Management

124

125

### Field Reducer

126

127

Core field state reducer for managing field values and validation.

128

129

```typescript { .api }

130

interface FieldAction {

131

type: 'UPDATE' | 'VALIDATE' | 'RESET' | 'REMOVE' | 'REPLACE_STATE';

132

path: string;

133

value?: unknown;

134

validate?: boolean;

135

errorMessage?: string;

136

disableFormData?: boolean;

137

}

138

139

function fieldReducer(

140

state: FormState,

141

action: FieldAction

142

): FormState;

143

144

interface FormState {

145

[path: string]: FieldState;

146

}

147

148

interface FieldState {

149

value: unknown;

150

valid: boolean;

151

errorMessage?: string;

152

initialValue?: unknown;

153

disableFormData?: boolean;

154

}

155

```

156

157

### Form Context Hooks

158

159

Access form state and actions through context hooks.

160

161

```typescript { .api }

162

function useFormFields<T>(

163

selector: (fields: FormFieldsContextType) => T

164

): T;

165

166

function useAllFormFields(): FormFieldsContextType;

167

168

function useFormSubmitted(): boolean;

169

function useFormProcessing(): boolean;

170

function useFormBackgroundProcessing(): boolean;

171

function useFormModified(): boolean;

172

function useFormInitializing(): boolean;

173

```

174

175

### Form Watch Hooks

176

177

Monitor form state changes.

178

179

```typescript { .api }

180

function useWatchForm<T>(): {

181

getDataByPath: (path: string) => unknown;

182

getData: () => Record<string, unknown>;

183

getSiblingData: (path: string) => Record<string, unknown>;

184

dispatchFields: (action: FieldAction) => void;

185

};

186

```

187

188

## Form Utilities

189

190

### withCondition

191

192

Higher-order component for conditional field rendering.

193

194

```typescript { .api }

195

function withCondition<T extends Record<string, unknown>>(

196

Component: React.ComponentType<T>

197

): React.ComponentType<T & ConditionalProps>;

198

199

interface ConditionalProps {

200

admin?: {

201

condition?: (data: Record<string, unknown>, siblingData?: Record<string, unknown>) => boolean;

202

};

203

}

204

```

205

206

### WatchCondition

207

208

Component for watching conditional field logic.

209

210

```typescript { .api }

211

interface WatchConditionProps {

212

path?: string;

213

condition: (data: Record<string, unknown>, siblingData?: Record<string, unknown>) => boolean;

214

children: React.ReactNode;

215

}

216

217

function WatchCondition(props: WatchConditionProps): JSX.Element | null;

218

```

219

220

### WatchChildErrors

221

222

Component for watching and handling child field errors.

223

224

```typescript { .api }

225

interface WatchChildErrorsProps {

226

path: string;

227

children?: React.ReactNode;

228

}

229

230

function WatchChildErrors(props: WatchChildErrorsProps): JSX.Element;

231

```

232

233

### NullifyLocaleField

234

235

Field component for nullifying locale-specific data.

236

237

```typescript { .api }

238

interface NullifyLocaleFieldProps {

239

path: string;

240

locale?: string;

241

}

242

243

function NullifyLocaleField(props: NullifyLocaleFieldProps): JSX.Element;

244

```

245

246

## Row Label Context

247

248

### RowLabelProvider

249

250

Context provider for row label data.

251

252

```typescript { .api }

253

interface RowLabelProviderProps {

254

children: React.ReactNode;

255

data: Record<string, unknown>;

256

index?: number;

257

path: string;

258

}

259

260

function RowLabelProvider(props: RowLabelProviderProps): JSX.Element;

261

```

262

263

### useRowLabel

264

265

Hook to access row label context data.

266

267

```typescript { .api }

268

function useRowLabel(): {

269

data: Record<string, unknown>;

270

index?: number;

271

path: string;

272

};

273

```

274

275

## Advanced Form Patterns

276

277

### Multi-Step Forms

278

279

```typescript

280

import { Form, useForm, SetStepNav } from '@payloadcms/ui';

281

282

function MultiStepForm() {

283

const { getData, validate } = useForm();

284

const [currentStep, setCurrentStep] = useState(0);

285

286

const steps = [

287

{ label: 'Basic Info', fields: basicFields },

288

{ label: 'Details', fields: detailFields },

289

{ label: 'Review', fields: [] }

290

];

291

292

const handleNext = () => {

293

if (validate()) {

294

setCurrentStep(prev => prev + 1);

295

}

296

};

297

298

return (

299

<Form>

300

<SetStepNav steps={steps} currentStep={currentStep} />

301

<RenderFields fields={steps[currentStep].fields} />

302

<button onClick={handleNext}>Next</button>

303

</Form>

304

);

305

}

306

```

307

308

### Dynamic Field Configuration

309

310

```typescript

311

import { RenderFields, useFormFields } from '@payloadcms/ui';

312

313

function ConditionalFields() {

314

const formData = useFormFields(fields => fields);

315

316

const getFieldsForType = (type: string) => {

317

switch (type) {

318

case 'article':

319

return articleFields;

320

case 'gallery':

321

return galleryFields;

322

default:

323

return baseFields;

324

}

325

};

326

327

const currentFields = getFieldsForType(formData.type?.value as string);

328

329

return <RenderFields fields={currentFields} />;

330

}

331

```

332

333

### Form Validation Patterns

334

335

```typescript

336

import { useField } from '@payloadcms/ui';

337

338

function ValidatedEmailField() {

339

const { value, setValue, showError, errorMessage } = useField<string>({

340

path: 'email',

341

validate: (val) => {

342

if (!val) return 'Email is required';

343

if (!/\S+@\S+\.\S+/.test(val)) return 'Invalid email format';

344

return true;

345

}

346

});

347

348

return (

349

<div>

350

<input

351

type="email"

352

value={value || ''}

353

onChange={(e) => setValue(e.target.value)}

354

/>

355

{showError && <span className="error">{errorMessage}</span>}

356

</div>

357

);

358

}

359

```

360

361

## Types

362

363

```typescript { .api }

364

interface FormFieldsContextType {

365

[path: string]: FieldState;

366

}

367

368

interface FieldState {

369

value: unknown;

370

valid: boolean;

371

errorMessage?: string;

372

initialValue?: unknown;

373

disableFormData?: boolean;

374

}

375

376

interface FieldConfig {

377

type: string;

378

name: string;

379

label?: string;

380

required?: boolean;

381

admin?: {

382

readOnly?: boolean;

383

disabled?: boolean;

384

hidden?: boolean;

385

condition?: (data: Record<string, unknown>) => boolean;

386

description?: string;

387

placeholder?: string;

388

};

389

validate?: (value: unknown, options: ValidateOptions) => string | true;

390

}

391

392

interface ValidateOptions {

393

data: Record<string, unknown>;

394

siblingData: Record<string, unknown>;

395

operation: 'create' | 'update';

396

id?: string | number;

397

}

398

399

interface ConditionalProps {

400

admin?: {

401

condition?: (

402

data: Record<string, unknown>,

403

siblingData?: Record<string, unknown>

404

) => boolean;

405

};

406

}

407

```