Error Handling
Handle errors gracefully in your Forma-powered forms.
FormaErrorBoundary
Wrap forms with FormaErrorBoundary to catch render errors:
import { FormRenderer, FormaErrorBoundary } from "@fogpipe/forma-react";
function MyForm() {
return (
<FormaErrorBoundary
fallback={<div>Something went wrong with the form</div>}
onError={(error) => console.error("Form error:", error)}
>
<FormRenderer
spec={mySpec}
components={components}
onSubmit={handleSubmit}
/>
</FormaErrorBoundary>
);
}
Props
| Prop | Type | Description |
|---|---|---|
fallback | ReactNode | (error, reset) => ReactNode | UI to show on error |
onError | (error: Error, errorInfo: React.ErrorInfo) => void | Callback when error occurs |
resetKey | string | number | Change to reset error state |
Fallback Options
Static Fallback
Simple error message:
<FormaErrorBoundary fallback={<div>Form failed to load</div>}>
{/* ... */}
</FormaErrorBoundary>
Function Fallback
Access error details and reset function:
<FormaErrorBoundary
fallback={(error, reset) => (
<div className="error-container">
<h3>Form Error</h3>
<p>{error.message}</p>
<button onClick={reset}>Try Again</button>
</div>
)}
>
{/* ... */}
</FormaErrorBoundary>
Error Logging
Log errors for debugging or monitoring:
<FormaErrorBoundary
onError={(error) => {
// Log to console
console.error("Form rendering error:", error);
// Send to error tracking service
errorTracker.captureException(error, {
context: "forma-form",
formId: spec.meta.id,
});
}}
fallback={<ErrorMessage />}
>
{/* ... */}
</FormaErrorBoundary>
Reset on Spec Change
Reset error state when the form specification changes:
function DynamicForm({ spec }) {
return (
<FormaErrorBoundary
resetKey={spec.meta.id} // Reset when form changes
fallback={<ErrorMessage />}
>
<FormRenderer spec={spec} components={components} />
</FormaErrorBoundary>
);
}
Validation Errors
Validation errors are different from render errors. They're handled through the form state:
Displaying Field Errors
const TextInput = ({ field }) => (
<div>
<input
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
className={field.errors.length > 0 ? "input-error" : ""}
/>
{field.errors.map((error, i) => (
<p key={i} className="error-message" role="alert">
{error.message}
</p>
))}
</div>
);
Accessing All Errors
function FormErrors({ forma }) {
if (forma.errors.length === 0) return null;
return (
<div className="form-errors" role="alert">
<h4>Please fix the following errors:</h4>
<ul>
{forma.errors.map((error, i) => (
<li key={i}>
<strong>{error.field}:</strong> {error.message}
</li>
))}
</ul>
</div>
);
}
Error Severity
Handle errors and warnings differently:
function FieldFeedback({ errors }) {
const fieldErrors = errors.filter((e) => e.severity === "error");
const warnings = errors.filter((e) => e.severity === "warning");
return (
<>
{fieldErrors.map((error, i) => (
<p key={`error-${i}`} className="error">
{error.message}
</p>
))}
{warnings.map((warning, i) => (
<p key={`warning-${i}`} className="warning">
{warning.message}
</p>
))}
</>
);
}
Submission Errors
Handle errors during form submission:
function SubmitHandler({ forma }) {
const [submitError, setSubmitError] = useState<string | null>(null);
const handleSubmit = async () => {
setSubmitError(null);
try {
await forma.submitForm();
} catch (error) {
setSubmitError(
error instanceof Error ? error.message : "An unexpected error occurred",
);
}
};
return (
<div>
{submitError && (
<div className="submit-error" role="alert">
{submitError}
</div>
)}
<button onClick={handleSubmit} disabled={forma.isSubmitting}>
{forma.isSubmitting ? "Submitting..." : "Submit"}
</button>
</div>
);
}
Invalid Specification Handling
Handle cases where the Forma specification might be invalid:
import { validateFormaComprehensive } from "@fogpipe/forma-core";
function SafeFormRenderer({ spec, ...props }) {
const validation = useMemo(() => validateFormaComprehensive(spec), [spec]);
if (!validation.valid) {
return (
<div className="spec-error">
<h3>Invalid Form Specification</h3>
<ul>
{validation.errors.map((error, i) => (
<li key={i}>{error.message}</li>
))}
</ul>
</div>
);
}
return (
<FormaErrorBoundary fallback={<div>Form error</div>}>
<FormRenderer spec={spec} {...props} />
</FormaErrorBoundary>
);
}
Best Practices
-
Always use error boundaries - Prevent form errors from crashing your app
-
Show meaningful messages - Help users understand what went wrong
-
Log errors - Track errors for debugging and monitoring
-
Handle all error types - Validation, submission, and render errors
-
Provide recovery options - Let users retry or reset the form
-
Validate specifications - Check specs before rendering in development