name: create-modus-form-component description: Scaffold form components with proper Modus input integration, event handling, validation, and checkbox bug handling
Create Modus Form Component
Scaffold form components with proper Modus input integration, event handling, validation feedback, and checkbox bug handling in SolidJS.
When to Use
Use this skill when:
- Creating forms with Modus input components
- Building multi-step forms
- Implementing form validation
- Handling form submissions
- Creating user registration or contact forms
Pattern Overview
Modus forms in SolidJS follow these patterns:
- Use Modus input components (ModusTextInput, ModusCheckbox, etc.)
- Handle input changes with proper event handlers
- Apply checkbox value inversion automatically (wrapper handles it)
- Use ModusInputFeedback for validation messages
- Include proper accessibility attributes
- Handle form submission with ModusButton
Basic Form Template
import { createSignal } from "solid-js";
import type { Component } from "solid-js";
import ModusTextInput from "./components/ModusTextInput";
import ModusCheckbox from "./components/ModusCheckbox";
import ModusButton from "./components/ModusButton";
import ModusInputFeedback from "./components/ModusInputFeedback";
import ModusInputLabel from "./components/ModusInputLabel";
interface FormData {
name: string;
email: string;
agreeToTerms: boolean;
}
const ContactForm: Component = () => {
const [formData, setFormData] = createSignal<FormData>({
name: "",
email: "",
agreeToTerms: false,
});
const [errors, setErrors] = createSignal<
Partial<Record<keyof FormData, string>>
>({});
const updateField = (field: keyof FormData, value: string | boolean) => {
setFormData((prev) => ({ ...prev, [field]: value }));
setErrors((prev) => {
if (prev[field]) {
return { ...prev, [field]: undefined };
}
return prev;
});
};
const validateForm = (): boolean => {
const data = formData();
const newErrors: Partial<Record<keyof FormData, string>> = {};
if (!data.name.trim()) {
newErrors.name = "Name is required";
}
if (!data.email.trim()) {
newErrors.email = "Email is required";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
newErrors.email = "Please enter a valid email address";
}
if (!data.agreeToTerms) {
newErrors.agreeToTerms = "You must agree to the terms";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = () => {
if (validateForm()) {
console.log("Form submitted:", formData());
}
};
return (
<div class="max-w-md mx-auto p-6 bg-card border-default rounded-lg">
<div class="text-2xl font-bold mb-6 text-foreground">Contact Us</div>
<div class="space-y-4">
{/* Name Field */}
<div>
<ModusInputLabel label="Name" required size="md" />
<ModusTextInput
value={formData().name}
onInputChange={(event) => {
const value = (event.target as HTMLModusWcTextInputElement).value;
updateField("name", value);
}}
placeholder="Enter your name"
required
size="md"
/>
{errors().name && (
<ModusInputFeedback
message={errors().name}
type="error"
size="md"
/>
)}
</div>
{/* Email Field */}
<div>
<ModusInputLabel label="Email" required size="md" />
<ModusTextInput
type="email"
value={formData().email}
onInputChange={(event) => {
const value = (event.target as HTMLModusWcTextInputElement).value;
updateField("email", value);
}}
placeholder="Enter your email"
required
size="md"
/>
{errors().email && (
<ModusInputFeedback
message={errors().email}
type="error"
size="md"
/>
)}
</div>
{/* Checkbox Field */}
<div>
<ModusCheckbox
label="I agree to the terms and conditions"
value={formData().agreeToTerms}
onValueChange={(event) => {
// ✅ Checkbox value is already corrected (inverted) by wrapper
updateField("agreeToTerms", event.detail);
}}
required
size="md"
/>
{errors().agreeToTerms && (
<ModusInputFeedback
message={errors().agreeToTerms}
type="error"
size="md"
/>
)}
</div>
{/* Submit Button */}
<div class="flex gap-2 pt-4">
<ModusButton color="primary" variant="filled" onButtonClick={handleSubmit}>
Submit
</ModusButton>
<ModusButton
variant="borderless"
onButtonClick={() => {
setFormData({ name: "", email: "", agreeToTerms: false });
setErrors({});
}}
>
Reset
</ModusButton>
</div>
</div>
</div>
);
};
export default ContactForm;
Key Patterns
1. Form State with createSignal
const [formData, setFormData] = createSignal<FormData>({
name: "",
email: "",
agreeToTerms: false,
});
2. Input Change Handling
// ✅ CORRECT: Extract value from event target
onInputChange={(event) => {
const value = (event.target as HTMLModusWcTextInputElement).value;
updateField("fieldName", value);
}}
3. Checkbox Value Handling
// ✅ CORRECT: Use event.detail (already corrected by wrapper)
onValueChange={(event) => {
updateField("checkboxField", event.detail);
}}
4. Validation Feedback
{errors().fieldName && (
<ModusInputFeedback
message={errors().fieldName}
type="error"
size="md"
/>
)}
5. SolidJS Class Attribute
Use class not className:
<div class="max-w-md mx-auto p-6 bg-card border-default rounded-lg">
Common Mistakes
- Double checkbox inversion: Don't invert checkbox values - wrapper handles it
- Wrong event value: Use
event.target.valuefor inputs, notevent.detail - Missing validation: Always validate required fields
- No error clearing: Clear errors when user starts typing
- Missing labels: Always provide labels for accessibility
Related Files
src/components/ModusTextInput.tsx- Text input componentsrc/components/ModusCheckbox.tsx- Checkbox component (handles bug)src/components/ModusTextarea.tsx- Textarea componentsrc/components/ModusInputFeedback.tsx- Validation feedbacksrc/components/ModusInputLabel.tsx- Input labels