Skip to main content

Default Components

The default component library ships a complete set of pre-built form components so you can render a working form in 2 lines of code.

When to use

Use default components when you want a working form fast — prototyping, internal tools, getting started. Use your own components when you need custom design systems, complex UX, or production branding. You can also start with defaults and progressively replace individual components.

Quick Start

npm install @fogpipe/forma-core @fogpipe/forma-react
ContactPage.tsx
import { DefaultFormRenderer } from "@fogpipe/forma-react/defaults";
import "@fogpipe/forma-react/defaults/styles.css";

function ContactPage() {
return (
<DefaultFormRenderer
spec={contactFormSpec}
onSubmit={(data) => console.log("Submitted:", data)}
/>
);
}

That's it. You get a fully styled, accessible form with validation, error display, and submit handling.

Override Individual Components

Swap any component while keeping the rest of the defaults:

CustomForm.tsx
import { FormRenderer } from "@fogpipe/forma-react";
import {
defaultComponentMap,
defaultFieldWrapper,
defaultLayout,
defaultPageWrapper,
} from "@fogpipe/forma-react/defaults";
import "@fogpipe/forma-react/defaults/styles.css";

// Replace just the select component
const components = { ...defaultComponentMap, select: MyFancySelect };

function CustomForm() {
return (
<FormRenderer
spec={formSpec}
components={components}
fieldWrapper={defaultFieldWrapper}
layout={defaultLayout}
pageWrapper={defaultPageWrapper}
onSubmit={handleSubmit}
/>
);
}

You can also import individual components for cherry-picking:

import {
TextInput,
SelectInput,
ArrayField,
} from "@fogpipe/forma-react/defaults";

Wizard Forms

For multi-page forms, set the wizardLayout prop:

SurveyForm.tsx
<DefaultFormRenderer
spec={multiPageSpec}
onSubmit={handleSubmit}
wizardLayout
/>

This gives you:

  • Step indicator with completed/current/upcoming states
  • Previous/Next/Submit buttons — Next and Submit are always separate (prevents accidental submission)
  • Page validation — Next button validates current page fields before advancing
  • Keyboard handling — Enter key on non-last pages triggers Next, not Submit

Programmatic Control

Forward a ref for imperative API access:

ProgrammaticForm.tsx
import { useRef } from "react";
import { DefaultFormRenderer } from "@fogpipe/forma-react/defaults";
import type { FormRendererHandle } from "@fogpipe/forma-react";

function ProgrammaticForm() {
const formRef = useRef<FormRendererHandle>(null);

return (
<>
<DefaultFormRenderer ref={formRef} spec={spec} onSubmit={handleSubmit} />
<button onClick={() => formRef.current?.submitForm()}>
External Submit
</button>
<button onClick={() => formRef.current?.resetForm()}>Reset</button>
</>
);
}

Theming

All styling is driven by CSS variables. Override them to match your brand:

custom-theme.css
:root {
--forma-color-primary: #7c3aed;
--forma-color-primary-hover: #6d28d9;
--forma-color-error: #e11d48;
--forma-radius: 12px;
--forma-font-family: "Inter", sans-serif;
}

Dark Mode

Dark mode works automatically via prefers-color-scheme, or manually with the data-theme attribute:

<!-- Automatic: follows system preference -->
<div>
<DefaultFormRenderer spec="{spec}" onSubmit="{handleSubmit}" />
</div>

<!-- Manual: force dark mode -->
<div data-theme="dark">
<DefaultFormRenderer spec="{spec}" onSubmit="{handleSubmit}" />
</div>

<!-- Manual: force light mode (overrides system dark preference) -->
<div data-theme="light">
<DefaultFormRenderer spec="{spec}" onSubmit="{handleSubmit}" />
</div>

CSS Variable Reference

Semantic Tokens

These are the primary variables consumers should override:

VariableDefaultDescription
--forma-font-familysystem-ui, -apple-system, sans-serifFont stack
--forma-font-size16pxBase font size
--forma-line-height1.5Base line height
--forma-color-primary#2563ebPrimary/accent color
--forma-color-primary-hover#1d4ed8Primary hover state
--forma-color-error#dc2626Error border/accent
--forma-color-warning#d97706Warning border/accent
--forma-color-border#d1d5dbInput border color
--forma-color-border-focus#2563ebFocused input border
--forma-color-bg#ffffffBackground color
--forma-color-bg-disabled#f9fafbDisabled field background
--forma-color-text#111827Primary text color
--forma-color-text-muted#6b7280Secondary/help text
--forma-color-text-error#dc2626Error message text
--forma-color-text-warning#d97706Warning message text
--forma-radius6pxBorder radius
--forma-spacing16pxBase spacing unit
--forma-spacing-sm8pxSmall spacing
--forma-spacing-xs4pxExtra-small spacing
--forma-transition150ms easeTransition timing

Component Tokens

These reference semantic tokens but can be overridden independently:

VariableDefaultDescription
--forma-input-bgvar(--forma-color-bg)Input background
--forma-input-bordervar(--forma-color-border)Input border
--forma-input-border-focusvar(--forma-color-border-focus)Input focus border
--forma-input-radiusvar(--forma-radius)Input border radius
--forma-input-padding0.5rem 0.75remInput padding
--forma-input-font-sizevar(--forma-font-size)Input font size
--forma-label-font-size0.875remLabel font size
--forma-label-font-weight500Label font weight
--forma-label-colorvar(--forma-color-text)Label text color
--forma-focus-ring-colorvar(--forma-color-border-focus)Focus ring color
--forma-focus-ring-width2pxFocus ring width
--forma-error-font-size0.8125remError text size
--forma-error-colorvar(--forma-color-text-error)Error text color
--forma-warning-colorvar(--forma-color-text-warning)Warning text color
--forma-button-bgvar(--forma-color-primary)Button background
--forma-button-bg-hovervar(--forma-color-primary-hover)Button hover
--forma-button-color#ffffffButton text color
--forma-button-radiusvar(--forma-radius)Button border radius
--forma-button-padding0.625rem 1.25remButton padding

Component Reference

Field TypeComponentRenders
text, email, phone, url, passwordTextInput<input> with type mapping (phonetel)
textareaTextareaInput<textarea> with 3 rows
numberNumberInput<input type="number"> — empty → null, NaN → null
integerIntegerInput<input type="number" step="1"> — uses parseInt
booleanBooleanInputCustom styled checkbox with inline label
dateDateInput<input type="date"> — native date picker
datetimeDateTimeInput<input type="datetime-local">
selectSelectInputNative <select> with custom arrow, placeholder option
multiselectMultiSelectInput<fieldset> with checkbox per option
arrayArrayFieldItem list with Add/Remove buttons, stable keys
objectObjectField<fieldset> with <legend>
computedComputedDisplayRead-only <output> element
displayDisplayFieldStatic text content
matrixMatrixField<table> with radio buttons (single) or checkboxes (multi)
fallbackFallbackFieldText input + dev-mode warning for unknown types

Layout Components

ComponentDescription
FieldWrapperRenders label, description, errors (touched-gated), required indicator. Skips label for boolean fields (they render their own).
FormLayout<form> with submit button. Submit is never disabled when invalid — clicking triggers validation so users can see what's wrong.
WizardLayoutMulti-page layout with step indicator, Previous/Next/Submit navigation, page validation. Falls back to FormLayout for single-page specs.
PageWrapperPage title (<h2>) and description.

Built-in Gotcha Prevention

The default components solve common pitfalls automatically:

ProblemSolution
Error messages shown before user interactsFieldWrapper gates errors on touched state — errors appear only after blur or form submission
Page refresh on form submitFormLayout calls e.preventDefault()
Wizard submit on Enter keyWizardLayout uses separate Next/Submit buttons with submitter detection
Number input returns stringNumberInput parses with parseFloat(), empty → null, NaN → null
Required indicator on boolean fieldsHidden unless field has explicit validation rules (consent checkbox pattern)
Array item key instabilityArrayField uses ref-based sequential counter for stable React keys

Exports

Everything is available from @fogpipe/forma-react/defaults:

// Convenience wrapper
import { DefaultFormRenderer } from "@fogpipe/forma-react/defaults";

// Component map and layout aliases
import {
defaultComponentMap,
defaultFieldWrapper,
defaultLayout,
defaultWizardLayout,
defaultPageWrapper,
} from "@fogpipe/forma-react/defaults";

// Individual components (for cherry-picking)
import {
TextInput,
TextareaInput,
NumberInput,
IntegerInput,
BooleanInput,
DateInput,
DateTimeInput,
SelectInput,
MultiSelectInput,
ArrayField,
ObjectField,
ComputedDisplay,
DisplayField,
MatrixField,
FallbackField,
} from "@fogpipe/forma-react/defaults";

// Layout components
import {
FieldWrapper,
FormLayout,
WizardLayout,
PageWrapper,
} from "@fogpipe/forma-react/defaults";

// CSS (import once, typically in your app root)
import "@fogpipe/forma-react/defaults/styles.css";

Next Steps

  • Component Mapping — Build custom components for full control
  • Styling Guide — Tailwind/CSS Modules patterns for custom components
  • Wizard Forms — Deep dive into multi-page form configuration
  • Events — React to form lifecycle events (analytics, data injection)