Getting Started
Components
- Advanced Toast
- ArrowButton
- Arrows
- Avatar
- Backdrop
- Basic Toast
- Breadcrumb
- Button
- CardBackground
- Card
- ChatButton
- Checkbox
- Checkboxes
- Chip
- ChoiceExpander
- Clickable
- Date Field
- Dropdown
- ElasticHeight
- ExpandableCard
- Filter
- FormCheckbox
- Form Control
- Form Label
- HamburgerIcon
- Heading
- Image
- Link
- Loader
- Number Field
- Pagination
- Password
- Phone Field
- Popin
- Portal
- Prose
- Radio Group
- Radio
- Range
- Select
- SidebarLayout
- Spinner
- Switch
- Tabs
- Tag
- TextField
- ValidationMessage
'use client';
import { FormControl } from '@/ui/forms/FormControl';
export function FormControlDemo() {
return (
<FormControl
id="email"
label="Email Address"
description="We'll never share your email"
required
className="max-w-md"
>
<input
id="email"
type="email"
placeholder="Enter your email"
className="rounded border border-grey px-12 py-8 w-full"
/>
</FormControl>
);
}
Installation
npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/form-control.json
Usage
'use client';
import { useState } from 'react';
import { FormControl } from '@/docs/lib/ui/forms/form-control';
export function Component() {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEmail(e.target.value);
if (!e.target.value.includes('@')) {
setError('Please enter a valid email address');
} else {
setError('');
}
};
return (
<FormControl
id="email"
label="Email Address"
description="We'll never share your email"
required
validationStatus={error ? 'error' : 'default'}
errorMessage={error}
className="max-w-md"
>
<input
id="email"
type="email"
value={email}
onChange={handleChange}
className="rounded border border-grey px-12 py-8 w-full"
/>
</FormControl>
);
}API Reference
FormControl
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | undefined | The form input element(s) to render inside the form control |
id | string | undefined | Unique identifier that links the label to the input element. Required when using a label |
label | ReactNode | undefined | Label text or content to display above the input. Only renders when both label and id are provided |
labelClassName | string | undefined | Additional CSS classes to apply to the FormLabel component |
description | string | undefined | Helper text displayed alongside the label |
disabled | boolean | undefined | When true, hides error messages and applies disabled state via useInternalStatus |
required | boolean | undefined | Marks the field as required and displays a required star indicator in the label |
hideRequiredStar | boolean | undefined | When true, hides the required star even when the field is required |
layout | "horizontal" | "vertical" | "horizontal-${string}" | undefined | Controls the layout orientation of the label. Passed to FormLabel component |
validationStatus | "default" | "error" | "success" | "default" | Visual validation state of the form control. Use "error" to display error messages |
errorMessage | string | undefined | Error message to display below the input when validationStatus is "error" and the field is not disabled |
dataTestId | string | undefined | Custom data-testid attribute for testing purposes |
dataName | string | undefined | Custom data-name attribute for styling and identification |
className | string | undefined | Additional CSS classes to apply to the outer container div |
value | Value | undefined | Generic value prop that can be passed through (type parameter, not commonly used directly) |
onChange | (name: string, value: Value) => void | undefined | Generic onChange handler (type parameter, not commonly used directly) |
The FormControl component also inherits props from FormLabelProps and supports generic type parameters for flexible value and attribute types.
Notes
- Layout container: FormControl provides a consistent flex column layout with gap-4 spacing between label, input, and error message
- Label integration: Automatically renders a FormLabel component when both
labelandidprops are provided - Conditional error display: Error messages only appear when
validationStatusis "error" and the field is not disabled - Internal status management: Uses the
useInternalStatushook to coordinate validation status and disabled state. When disabled, the internal status becomes "disabled" which hides error messages - Generic type support: Supports generic type parameters for flexible value types and HTML attribute types, making it reusable across different input types
- FormLabel delegation: Passes
description,layout,required,hideRequiredStar, andlabelClassNameprops to the FormLabel component for consistent label rendering - Error component: Uses FormControlError component to display error messages with proper styling and accessibility attributes
- Data attributes: Supports custom
data-nameanddata-testidattributes for styling and testing - Composition pattern: Designed as a composition wrapper that accepts any form input as children, providing flexibility for different input types (text, select, textarea, etc.)
- Accessibility: Properly links labels to inputs via the
idprop, ensuring screen readers can associate labels with their inputs - Client component: This is a client component that requires the
"use client"directive due to the useInternalStatus hook