Overview
FormFooter provides a consistent footer pattern for forms across all containers (PageCard, PropertyPanel, Dialog). It features a 3-zone layout: left actions (Cancel, Reset), center status (messages, saving indicators), and right actions (primary submit).
Key Features
- Three-zone layout: left actions, center status, right actions
- formId pattern for connecting to forms outside the footer
- Built-in status messages (info, success, warning, error)
- Consistent styling across all containers
Examples
Basic Usage
Simple cancel/save footer with formId connection to FormBlock.
With Status Message
Center zone displays status messages for validation, saving state, or unsaved changes.
Center zone displays status messages for validation, saving state, or unsaved changes.
In PropertyPanel Overlay
FormFooter works seamlessly with PropertyPanel overlay mode using the formId pattern.
Multiple Actions
Both left and right zones support multiple buttons.
Both left and right zones support multiple buttons.
Fixed to Viewport
Viewport-fixed footer for standalone forms outside containers. Opens in an overlay to demonstrate.
Use the fixed prop for forms that need a permanently visible footer locked to the bottom of the viewport. This is ideal for wizard preview steps, full-page forms, or any context where the footer should always be accessible regardless of scroll position.
Floating Footer
Sticky footer with floating card appearance within scroll container.
Use the floating prop for footers that should stick within a scroll container but have a floating card appearance with rounded corners, shadow, and margins. This is ideal for forms in LayoutShell where the footer should feel like a floating action bar.
Scroll down to see the floating footer behavior. The footer stays sticky at the bottom of this scroll container with a floating card appearance.
Sample content block 1 - scroll to see floating footer
Sample content block 2 - scroll to see floating footer
Sample content block 3 - scroll to see floating footer
Sample content block 4 - scroll to see floating footer
Sample content block 5 - scroll to see floating footer
Sample content block 6 - scroll to see floating footer
Props Reference
| Prop | Type | Default | Description |
|---|---|---|---|
formId | string | - | Form ID to connect submit button via HTML form attribute |
submitLabel | string | "Save" | Submit button label |
cancelLabel | string | - | Cancel button label (shown if onCancel provided) |
onCancel | () => void | - | Cancel button handler |
onSubmit | () => void | - | Submit handler (only when formId not provided) |
isSubmitting | boolean | false | Whether form is currently submitting |
disabled | boolean | false | Disable all buttons |
leftActions | ReactNode | - | Custom left zone content (replaces cancel button) |
rightActions | ReactNode | - | Custom right zone content (replaces submit button) |
status | { type, message } | - | Status message object with type and message |
centerContent | ReactNode | - | Custom center zone content (replaces status) |
sticky | boolean | false | Sticky to bottom of scroll container |
floating | boolean | false | Floating card appearance, sticky within scroll container |
fixed | boolean | false | Fixed to bottom of viewport (needs pb-20 on content) |
maxWidth | string | "max-w-4xl" | Max width class for content alignment (only with fixed) |
fullWidth | boolean | false | Buttons fill available width evenly |
Usage
import { FormFooter, PropertyPanel, PropertyPanelContent } from '@/ui/components';
import { FormBlock, FormTextField } from '@/ui/blocks';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1, 'Name is required'),
});
function MySheetForm() {
const [open, setOpen] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (data) => {
setIsSubmitting(true);
try {
await saveData(data);
setOpen(false);
toast.success('Saved successfully');
} finally {
setIsSubmitting(false);
}
};
return (
<PropertyPanel
overlay
open={open}
onOpenChange={setOpen}
title="Edit Settings"
footer={
<FormFooter
formId="settings-form"
cancelLabel="Cancel"
onCancel={() => setOpen(false)}
submitLabel="Save"
isSubmitting={isSubmitting}
/>
}
>
<PropertyPanelContent>
<FormBlock
id="settings-form"
schema={schema}
onSubmit={handleSubmit}
hideActions
>
<FormTextField name="name" label="Name" />
</FormBlock>
</PropertyPanelContent>
</PropertyPanel>
);
}