| name | oxygen-form |
| description | Generate forms with Form.* components and validation. Use when creating input forms, multi-step wizards, or form validation. |
Generate Oxygen UI Form
Instructions
- Read
.claude/oxygen-ui/components.md for Form.* API
- Use
Form.Section for grouping fields
- Use
Form.Stack for field layout
- Add validation with
error and helperText props
Critical Rules
- Import form components from
@wso2/oxygen-ui
- Use controlled components with state
- Show validation errors with
error and helperText props
- Use theme tokens for spacing
Basic Form
import { useState } from 'react';
import { Box, TextField, Button, Alert } from '@wso2/oxygen-ui';
function BasicForm() {
const [formData, setFormData] = useState({ name: '', email: '' });
const [errors, setErrors] = useState<Record<string, string>>({});
const validate = () => {
const newErrors: Record<string, string> = {};
if (!formData.name) {
newErrors.name = 'Name is required';
}
if (!formData.email) {
newErrors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = 'Invalid email format';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (validate()) {
console.log('Form submitted:', formData);
}
};
return (
<Box component="form" onSubmit={handleSubmit} sx={{ maxWidth: 400 }}>
<TextField
fullWidth
label="Name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
error={!!errors.name}
helperText={errors.name}
margin="normal"
required
/>
<TextField
fullWidth
label="Email"
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
error={!!errors.email}
helperText={errors.email}
margin="normal"
required
/>
<Button type="submit" variant="contained" sx={{ mt: 2 }}>
Submit
</Button>
</Box>
);
}
Form with Sections
import { Form, TextField, Button, Box } from '@wso2/oxygen-ui';
function SectionedForm() {
return (
<Box sx={{ maxWidth: 600 }}>
<Form.Section>
<Form.Header>Personal Information</Form.Header>
<Form.Subheader>Enter your basic details</Form.Subheader>
<Form.Stack spacing={2}>
<Form.TextInput label="First Name" required />
<Form.TextInput label="Last Name" required />
<Form.TextInput label="Email" type="email" required />
</Form.Stack>
</Form.Section>
<Form.Section>
<Form.Header>Company Details</Form.Header>
Optional information about your organization
Developer
Designer
Manager
Cancel
Save
);
}
Login Form
import { useState } from 'react';
import {
Box,
Paper,
Typography,
TextField,
Button,
Link,
Divider,
Alert,
} from '@wso2/oxygen-ui';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError('');
try {
await login(email, password);
} catch (err) {
setError('Invalid credentials');
} finally {
setLoading();
}
};
(
);
}
Multi-Step Wizard
import { useState } from 'react';
import {
Box,
Paper,
Stepper,
Step,
StepLabel,
Button,
Typography,
TextField,
} from '@wso2/oxygen-ui';
const steps = ['Account', 'Profile', 'Review'];
function RegistrationWizard() {
const [activeStep, setActiveStep] = useState(0);
const [formData, setFormData] = useState({
email: '',
password: '',
name: '',
company: '',
});
const handleNext = () => setActiveStep((prev) => prev + 1);
const handleBack = () => setActiveStep((prev) => prev - 1);
const handleSubmit = () => {
console.log(, formData);
};
= () => {
(step) {
:
(
);
:
(
);
:
(
);
:
;
}
};
(
);
}
Form with Select and Autocomplete
import { useState } from 'react';
import { Box, TextField, Button, MenuItem, Autocomplete } from '@wso2/oxygen-ui';
const countries = [
{ label: 'United States', code: 'US' },
{ label: 'United Kingdom', code: 'UK' },
{ label: 'Canada', code: 'CA' },
];
function AddressForm() {
const [country, setCountry] = useState<typeof countries[0] | null>(null);
const [state, setState] = useState('');
return (
<Box sx={{ maxWidth: 400 }}>
<Autocomplete
options={countries}
value={country}
onChange={(_, newValue) => setCountry(newValue)}
renderInput={(params) => (
<TextField {} = = />
)}
/>
setState(e.target.value)}
margin="normal"
>
California
New York
Texas
Save Address
);
}