FormModal
A modal that renders a DynamicForm inside a Paper card. It supports fetching initial data from an API (fetchPath), submitting via configurable HTTP methods (saveMethod), and mode-aware callbacks for create, edit, and field-level edits. Errors are surfaced via an inline Toast. The form is deferred-mounted until the modal's open animation finishes.
Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| isOpen | boolean | Yes | — | Controls whether the modal is visible. |
| onClose | (reason: 'cancel' \| 'success' \| 'error' \| 'custom') => void | Yes | — | Called when the modal closes. Receives a reason string. |
| fields | Field[] | Yes | — | Form field definitions. Each field has type, name, and optional label, config, value. |
| backdropStyle | React.CSSProperties | No | — | Inline styles for the modal backdrop. |
| windowStyle | React.CSSProperties | No | — | Inline styles merged into the modal window. |
| closeButtonStyle | React.CSSProperties | No | — | Styles for the close button. |
| closeIcon | string | No | — | Icon name for the close button. |
| closeIconPaths | any[] | No | — | Custom icon paths for the close button. |
| closeIconSize | number | No | — | Icon size for the close button. |
| zIndex | number | No | — | CSS z-index of the modal overlay. |
| id | string | No | — | HTML id for the modal element. |
| fullScreen | boolean | No | — | When true, the modal occupies the full viewport. |
| formTitle | string | No | — | Title displayed on the form card. |
| data | Record<string, any> | No | — | Initial field values. If data.formatted_id exists, it is shown above the card. |
| fetchPath | string | No | — | API path to prefetch form data before rendering the form. |
| savePath | string | No | — | API path for form submission. |
| onSave | (formData: Record<string, any>) => void | No | — | Called instead of an API request when savePath is not provided. |
| onCreateSuccess | (formData: Record<string, any>) => void | No | — | Called after a successful create submission. |
| onCreateError | (error: any) => void | No | — | Called after a failed create submission. |
| onEditSuccess | (formData: Record<string, any>) => void | No | — | Called after a successful edit submission. |
| onEditError | (error: any) => void | No | — | Called after a failed edit submission. |
| onFieldEditSuccess | (field: any, fieldValue: any, formData: Record<string, any>) => void | No | — | Called after a successful individual field edit. |
| onFieldEditError | (field: any, error: any) => void | No | — | Called after a failed individual field edit. |
| extraData | Record<string, any> | No | — | Extra fields merged into the submission payload. |
| mode | "create" \| "edit" \| "globalEdit" \| "readOnly" \| "submit" \| "view" | No | — | Form mode passed to DynamicForm. |
| apiBaseUrl | string | No | urls.apiBase | Base URL for the HTTP client. |
| saveMethod | 'POST' \| 'PUT' \| 'PATCH' | No | 'POST' | HTTP method used for form submission. |
| useAuthToken | boolean | No | — | When true, uses the authenticated HTTP client. |
| formWidth | number \| string | No | 700 | Max width of the form card. |
| formContainerStyle | React.CSSProperties | No | — | Styles for the form container. |
| formHeaderStyle | React.CSSProperties | No | — | Styles for the form header section. |
| formBodyStyle | React.CSSProperties | No | — | Styles for the form body section. |
| formTitleStyle | React.CSSProperties | No | — | Styles for the form title. |
| fieldContainerStyle | React.CSSProperties | No | — | Styles for each field's container. |
| fieldLabelStyle | React.CSSProperties | No | — | Styles for field labels. |
| fieldDescriptionStyle | React.CSSProperties | No | — | Styles for field descriptions. |
| fieldHeaderStyle | React.CSSProperties | No | — | Styles for each field's header. |
| fieldBodyStyle | React.CSSProperties | No | — | Styles for each field's body. |
| className | string | No | — | CSS class applied to the form. |
| sendButtonType | "clear" \| "outline" \| "solid" | No | — | Visual variant of the submit button. |
| sendButtonSize | "xs" \| "sm" \| "md" \| "lg" \| "xl" | No | — | Size of the submit button. |
| sendButtonColor | string | No | — | Color of the submit button. |
| sendButtonTitle | string | No | — | Label for the submit button. |
| sendButtonIcon | string | No | — | Icon name for the submit button. |
| sendButtonIconPaths | any[] | No | — | Custom icon paths for the submit button. |
| sendButtonIconSize | number | No | — | Icon size for the submit button. |
| sendButtonStyle | React.CSSProperties | No | — | Inline styles for the submit button. |
| sendButtonTitleStyle | React.CSSProperties | No | — | Inline styles for the submit button label. |
Usage
Basic
import React from 'react';
import FormModal from '@/components/modals/FormModal';
export default function Example() {
const [open, setOpen] = React.useState(false);
return (
<>
<button onClick={() => setOpen(true)}>Edit Product</button>
<FormModal
isOpen={open}
onClose={(reason) => {
console.log('Closed:', reason);
setOpen(false);
}}
formTitle="Edit Product"
fields={[
{ type: 'text', name: 'name', label: 'Name' },
{ type: 'number', name: 'price', label: 'Price' },
]}
data={{ name: 'Blue T-Shirt', price: 29.99 }}
savePath="/v1/products/123"
saveMethod="PATCH"
mode="edit"
useAuthToken
onEditSuccess={(data) => console.log('Updated:', data)}
/>
</>
);
}