or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-setup.mdauthentication.mddata-operations.mdforms.mdindex.mdnavigation.mdtables-lists.mdutilities.md

forms.mddocs/

0

# Forms & Data Input

1

2

Advanced form management with auto-save, validation, mutation handling, and integration with popular form libraries.

3

4

## Capabilities

5

6

### Core Form Management

7

8

#### useForm Hook

9

10

Orchestrates data hooks for create, edit, and clone operations with advanced features like auto-save, validation, and automatic redirects.

11

12

```typescript { .api }

13

/**

14

* Orchestrates form operations with data management, validation, and auto-save

15

* @param params - Form configuration options

16

* @returns Form state, handlers, and mutation controls

17

*/

18

function useForm<TQueryFnData = BaseRecord, TError = HttpError, TVariables = {}, TData = TQueryFnData, TResponse = BaseRecord, TResponseError = HttpError>(

19

params?: UseFormConfig<TQueryFnData, TError, TVariables, TData, TResponse, TResponseError>

20

): UseFormReturnType<TQueryFnData, TError, TVariables, TData, TResponse, TResponseError>;

21

22

interface UseFormConfig<TQueryFnData, TError, TVariables, TData, TResponse, TResponseError> {

23

/** Form action type - determines behavior */

24

action?: "create" | "edit" | "clone";

25

/** Resource name - inferred from route if not provided */

26

resource?: string;

27

/** Record ID for edit/clone operations */

28

id?: BaseKey;

29

/** Where to redirect after successful submission */

30

redirect?: RedirectAction;

31

/** Additional metadata for operations */

32

meta?: MetaQuery;

33

/** Mutation mode for optimistic updates */

34

mutationMode?: MutationMode;

35

/** Callback on successful mutation */

36

onMutationSuccess?: (data: TResponse, variables: TVariables, context: any, isAutoSave: boolean) => void;

37

/** Callback on mutation error */

38

onMutationError?: (error: TResponseError, variables: TVariables, context: any, isAutoSave: boolean) => void;

39

/** Auto-save configuration */

40

autoSave?: {

41

/** Enable auto-save functionality */

42

enabled?: boolean;

43

/** Debounce delay in milliseconds */

44

debounce?: number;

45

/** Invalidate queries when component unmounts */

46

invalidateOnUnmount?: boolean;

47

};

48

/** Success notification configuration */

49

successNotification?: SuccessErrorNotification | false;

50

/** Error notification configuration */

51

errorNotification?: SuccessErrorNotification | false;

52

/** Data provider name to use */

53

dataProviderName?: string;

54

/** Query options for data fetching */

55

queryOptions?: UseQueryOptions<TQueryFnData, TError>;

56

/** Mutation options for create/update */

57

createMutationOptions?: UseMutationOptions<TResponse, TResponseError, UseCreateParams<TVariables>>;

58

/** Mutation options for update operations */

59

updateMutationOptions?: UseMutationOptions<TResponse, TResponseError, UseUpdateParams<TVariables>>;

60

/** Timeout for undoable mutations */

61

undoableTimeout?: number;

62

/** Cache invalidation configuration */

63

invalidates?: Array<string>;

64

}

65

66

interface UseFormReturnType<TQueryFnData, TError, TVariables, TData, TResponse, TResponseError> {

67

/** Form submission handler */

68

onFinish: (values: TVariables) => Promise<void>;

69

/** Auto-save handler for form changes */

70

onFinishAutoSave: (values: TVariables) => Promise<void>;

71

/** Whether form operations are loading */

72

formLoading: boolean;

73

/** Mutation result for create/update operations */

74

mutation: UseMutationResult<TResponse, TResponseError, UseCreateParams<TVariables> | UseUpdateParams<TVariables>>;

75

/** Query result for fetching existing data (edit/clone) */

76

query: UseQueryResult<TQueryFnData, TError>;

77

/** Auto-save props for form libraries */

78

autoSaveProps: AutoSaveProps;

79

/** Current record ID */

80

id?: BaseKey;

81

/** Function to set record ID */

82

setId: React.Dispatch<React.SetStateAction<BaseKey | undefined>>;

83

/** Redirect configuration */

84

redirect: RedirectAction;

85

/** Loading overtime information */

86

overtime: UseLoadingOvertimeReturnType;

87

}

88

89

interface AutoSaveProps {

90

/** Current auto-save status */

91

status: "loading" | "success" | "error" | "idle";

92

/** Error from auto-save operation */

93

error?: TResponseError;

94

/** Data from successful auto-save */

95

data?: TResponse;

96

}

97

```

98

99

**Usage Example:**

100

101

```typescript

102

import { useForm } from "@refinedev/core";

103

import { useEffect } from "react";

104

105

interface PostFormData {

106

title: string;

107

content: string;

108

status: "draft" | "published";

109

categoryId: number;

110

}

111

112

function PostForm() {

113

const {

114

onFinish,

115

onFinishAutoSave,

116

formLoading,

117

query,

118

autoSaveProps,

119

id,

120

setId

121

} = useForm<PostFormData>({

122

action: "edit", // or "create", "clone"

123

resource: "posts",

124

redirect: "list",

125

autoSave: {

126

enabled: true,

127

debounce: 2000 // Auto-save after 2 seconds of inactivity

128

},

129

onMutationSuccess: (data, variables, context, isAutoSave) => {

130

if (isAutoSave) {

131

console.log("Auto-saved successfully");

132

} else {

133

console.log("Form submitted successfully");

134

}

135

}

136

});

137

138

const [formData, setFormData] = useState<PostFormData>({

139

title: "",

140

content: "",

141

status: "draft",

142

categoryId: 1

143

});

144

145

// Load existing data for edit mode

146

useEffect(() => {

147

if (query.data) {

148

setFormData(query.data.data);

149

}

150

}, [query.data]);

151

152

// Auto-save on form changes

153

useEffect(() => {

154

const timeoutId = setTimeout(() => {

155

onFinishAutoSave(formData);

156

}, 2000);

157

158

return () => clearTimeout(timeoutId);

159

}, [formData, onFinishAutoSave]);

160

161

const handleSubmit = async (e: React.FormEvent) => {

162

e.preventDefault();

163

await onFinish(formData);

164

};

165

166

return (

167

<form onSubmit={handleSubmit}>

168

<div>

169

<label>Title:</label>

170

<input

171

value={formData.title}

172

onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}

173

disabled={formLoading}

174

/>

175

</div>

176

177

<div>

178

<label>Content:</label>

179

<textarea

180

value={formData.content}

181

onChange={(e) => setFormData(prev => ({ ...prev, content: e.target.value }))}

182

disabled={formLoading}

183

/>

184

</div>

185

186

<div>

187

<label>Status:</label>

188

<select

189

value={formData.status}

190

onChange={(e) => setFormData(prev => ({ ...prev, status: e.target.value as "draft" | "published" }))}

191

disabled={formLoading}

192

>

193

<option value="draft">Draft</option>

194

<option value="published">Published</option>

195

</select>

196

</div>

197

198

{/* Auto-save indicator */}

199

<div className="auto-save-status">

200

{autoSaveProps.status === "loading" && <span>Saving...</span>}

201

{autoSaveProps.status === "success" && <span>Saved ✓</span>}

202

{autoSaveProps.status === "error" && <span>Save failed ✗</span>}

203

</div>

204

205

<button type="submit" disabled={formLoading}>

206

{formLoading ? "Submitting..." : "Submit"}

207

</button>

208

</form>

209

);

210

}

211

```

212

213

### Form Library Integrations

214

215

#### React Hook Form Integration

216

217

Integration patterns with React Hook Form for advanced form validation and state management.

218

219

```typescript { .api }

220

/**

221

* Integration with React Hook Form

222

* @param params - Configuration for React Hook Form integration

223

* @returns Combined form state and Refine form handlers

224

*/

225

function useFormWithReactHookForm<TFormData, TQueryFnData = BaseRecord, TError = HttpError, TResponse = BaseRecord>(

226

params?: UseFormConfig<TQueryFnData, TError, TFormData, TQueryFnData, TResponse, TError>

227

): UseFormWithReactHookFormReturnType<TFormData, TQueryFnData, TError, TResponse>;

228

229

interface UseFormWithReactHookFormReturnType<TFormData, TQueryFnData, TError, TResponse> extends UseFormReturnType<TQueryFnData, TError, TFormData, TQueryFnData, TResponse, TError> {

230

/** Register form fields with React Hook Form */

231

register: UseFormRegister<TFormData>;

232

/** Handle form submission with validation */

233

handleSubmit: UseFormHandleSubmit<TFormData>;

234

/** Form state and errors */

235

formState: FormState<TFormData>;

236

/** Set form values programmatically */

237

setValue: UseFormSetValue<TFormData>;

238

/** Get form values */

239

getValues: UseFormGetValues<TFormData>;

240

/** Watch form field changes */

241

watch: UseFormWatch<TFormData>;

242

}

243

```

244

245

**React Hook Form Example:**

246

247

```typescript

248

import { useForm as useRefineForm } from "@refinedev/core";

249

import { useForm as useReactHookForm } from "react-hook-form";

250

import { yupResolver } from "@hookform/resolvers/yup";

251

import * as yup from "yup";

252

253

const schema = yup.object({

254

title: yup.string().required("Title is required"),

255

content: yup.string().min(10, "Content must be at least 10 characters"),

256

email: yup.string().email("Invalid email format")

257

});

258

259

function PostFormWithValidation() {

260

const { onFinish, formLoading, query } = useRefineForm();

261

262

const {

263

register,

264

handleSubmit,

265

formState: { errors },

266

setValue,

267

watch

268

} = useReactHookForm({

269

resolver: yupResolver(schema),

270

defaultValues: query.data?.data

271

});

272

273

const onSubmit = (data: any) => {

274

onFinish(data);

275

};

276

277

return (

278

<form onSubmit={handleSubmit(onSubmit)}>

279

<div>

280

<input {...register("title")} placeholder="Title" />

281

{errors.title && <span>{errors.title.message}</span>}

282

</div>

283

284

<div>

285

<textarea {...register("content")} placeholder="Content" />

286

{errors.content && <span>{errors.content.message}</span>}

287

</div>

288

289

<button type="submit" disabled={formLoading}>

290

Submit

291

</button>

292

</form>

293

);

294

}

295

```

296

297

#### Formik Integration

298

299

Integration patterns with Formik for form state management and validation.

300

301

```typescript { .api }

302

/**

303

* Integration with Formik

304

* @param params - Configuration for Formik integration

305

* @returns Formik props combined with Refine form handlers

306

*/

307

function useFormWithFormik<TFormData>(

308

params?: UseFormConfig & FormikConfig<TFormData>

309

): UseFormWithFormikReturnType<TFormData>;

310

311

interface UseFormWithFormikReturnType<TFormData> {

312

/** Formik form props */

313

formikProps: FormikProps<TFormData>;

314

/** Refine form handlers */

315

refineProps: UseFormReturnType;

316

}

317

```

318

319

### Auto-Save & Draft Management

320

321

#### AutoSaveIndicator Component

322

323

Visual indicator for auto-save status with customizable appearance.

324

325

```typescript { .api }

326

/**

327

* Visual indicator for auto-save status

328

* @param props - Auto-save indicator configuration

329

* @returns Auto-save status indicator component

330

*/

331

function AutoSaveIndicator(props?: AutoSaveIndicatorProps): JSX.Element;

332

333

interface AutoSaveIndicatorProps {

334

/** Auto-save status */

335

status: "loading" | "success" | "error" | "idle";

336

/** Custom messages for each status */

337

messages?: {

338

loading?: string;

339

success?: string;

340

error?: string;

341

idle?: string;

342

};

343

/** Custom styling */

344

style?: React.CSSProperties;

345

/** Custom class names */

346

className?: string;

347

}

348

```

349

350

**Usage Example:**

351

352

```typescript

353

import { AutoSaveIndicator, useForm } from "@refinedev/core";

354

355

function FormWithAutoSave() {

356

const { autoSaveProps, onFinishAutoSave } = useForm({

357

autoSave: { enabled: true, debounce: 1000 }

358

});

359

360

return (

361

<div>

362

<form>

363

{/* Form fields */}

364

</form>

365

366

<AutoSaveIndicator

367

status={autoSaveProps.status}

368

messages={{

369

loading: "Saving draft...",

370

success: "Draft saved",

371

error: "Failed to save draft"

372

}}

373

/>

374

</div>

375

);

376

}

377

```

378

379

### Form Validation

380

381

#### Client-Side Validation

382

383

Integration with validation libraries for client-side form validation.

384

385

```typescript { .api }

386

/**

387

* Validation configuration for forms

388

*/

389

interface ValidationConfig<TFormData> {

390

/** Validation schema */

391

schema?: ValidationSchema<TFormData>;

392

/** Custom validation functions */

393

rules?: ValidationRules<TFormData>;

394

/** Validation mode */

395

mode?: "onChange" | "onBlur" | "onSubmit";

396

/** Re-validation mode */

397

reValidateMode?: "onChange" | "onBlur" | "onSubmit";

398

}

399

400

interface ValidationSchema<TFormData> {

401

[K in keyof TFormData]?: ValidationRule[];

402

}

403

404

interface ValidationRule {

405

/** Validation type */

406

type: "required" | "email" | "min" | "max" | "pattern" | "custom";

407

/** Validation value */

408

value?: any;

409

/** Error message */

410

message: string;

411

}

412

413

interface ValidationRules<TFormData> {

414

[K in keyof TFormData]?: (value: TFormData[K], formData: TFormData) => string | undefined;

415

}

416

```

417

418

### Form State Management

419

420

#### Form Loading States

421

422

Managing different loading states during form operations.

423

424

```typescript { .api }

425

/**

426

* Form loading state management

427

*/

428

interface FormLoadingState {

429

/** Whether form is submitting */

430

submitting: boolean;

431

/** Whether data is being fetched */

432

fetching: boolean;

433

/** Whether auto-save is in progress */

434

autoSaving: boolean;

435

/** Whether form is validating */

436

validating: boolean;

437

}

438

```

439

440

#### Form Error Handling

441

442

Comprehensive error handling for form operations.

443

444

```typescript { .api }

445

/**

446

* Form error handling configuration

447

*/

448

interface FormErrorHandling<TError> {

449

/** Field-specific errors */

450

fieldErrors?: Record<string, string[]>;

451

/** General form errors */

452

formErrors?: string[];

453

/** Server validation errors */

454

serverErrors?: TError;

455

/** Error display mode */

456

errorMode?: "field" | "summary" | "both";

457

}

458

```

459

460

### Advanced Form Features

461

462

#### Multi-Step Forms

463

464

Support for multi-step form workflows with data persistence.

465

466

```typescript { .api }

467

/**

468

* Multi-step form management

469

*/

470

interface MultiStepFormConfig {

471

/** Current step index */

472

currentStep: number;

473

/** Total number of steps */

474

totalSteps: number;

475

/** Step validation configuration */

476

stepValidation?: Record<number, ValidationConfig>;

477

/** Data persistence between steps */

478

persistData?: boolean;

479

}

480

481

interface MultiStepFormActions {

482

/** Go to next step */

483

nextStep: () => void;

484

/** Go to previous step */

485

previousStep: () => void;

486

/** Go to specific step */

487

goToStep: (step: number) => void;

488

/** Complete the form */

489

complete: () => void;

490

}

491

```

492

493

#### Dynamic Forms

494

495

Support for forms with dynamic field generation and conditional logic.

496

497

```typescript { .api }

498

/**

499

* Dynamic form field configuration

500

*/

501

interface DynamicFieldConfig {

502

/** Field type */

503

type: "text" | "number" | "select" | "checkbox" | "radio" | "textarea" | "date";

504

/** Field name/key */

505

name: string;

506

/** Display label */

507

label: string;

508

/** Default value */

509

defaultValue?: any;

510

/** Field options for select/radio */

511

options?: Array<{ label: string; value: any }>;

512

/** Validation rules */

513

validation?: ValidationRule[];

514

/** Conditional display logic */

515

condition?: (formData: any) => boolean;

516

/** Field-specific props */

517

props?: Record<string, any>;

518

}

519

```

520

521

## Types

522

523

```typescript { .api }

524

type RedirectAction = "list" | "edit" | "show" | "create" | "clone" | false;

525

526

interface UseLoadingOvertimeReturnType {

527

/** Elapsed time since loading started */

528

elapsedTime?: number;

529

}

530

531

interface FormSubmissionResult<TData> {

532

/** Whether submission was successful */

533

success: boolean;

534

/** Response data */

535

data?: TData;

536

/** Error information */

537

error?: any;

538

/** Redirect configuration */

539

redirect?: {

540

to: string;

541

type?: "push" | "replace";

542

};

543

}

544

545

interface FormFieldProps {

546

/** Field name */

547

name: string;

548

/** Field value */

549

value: any;

550

/** Change handler */

551

onChange: (value: any) => void;

552

/** Blur handler */

553

onBlur?: () => void;

554

/** Field error */

555

error?: string;

556

/** Whether field is disabled */

557

disabled?: boolean;

558

/** Whether field is required */

559

required?: boolean;

560

}

561

```