smithery.ai

generate-form

Generate form component with validation, state management, and submit handling. Use when creating new data entry forms.

First seen Apr 17, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsRead, Write, Glob, Grep

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,450 B
  • docs SUMMARY.md 140 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Generate Form

Generate a form component with validation and store integration.

Usage

When user requests to create a form component, ask for:

  1. Form name (e.g., "WaterIntakeForm", "SleepLogForm")
  2. Fields needed (name, type, validation rules)
  3. Store action to call on submit (e.g., "addWaterLog")
  4. Whether it includes a list (add/remove items like MealLogForm)

Implementation Pattern

Based on src/components/forms/MealLogForm.tsx pattern.

File Structure

Create file: src/components/forms/{FormName}.tsx

'use client';

import { useState } from 'react';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { Trash2, Plus, Loader2 } from 'lucide-react';
import { useHealthStore } from '@/lib/store/healthStore';
import { toast } from 'sonner';

export function FormName() {
  const [field1, setField1] = useState('');
  const [field2, setField2] = useState('');
  const { addItem, isLoading } = useHealthStore();
  const [errors, setErrors] = useState<Record<string, string>>({});

  const validate = () => {
    const newErrors: Record<string, string> = {};

    if (!field1.trim()) {
      newErrors.field1 = 'Field 1 is required';
    }

    if (!field2.trim()) {
      newErrors.field2 = 'Field 2 is required';
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = async () => {
    if (!validate()) return;

    const today = new Date().toISOString().split('T')[0];
    try {
      await addItem({
        date: today,
        field1,
        field2,
      });
      setField1('');
      setField2('');
      setErrors({});
    } catch (error: any) {
      toast.error(error.message || 'Failed to submit form');
    }
  };

  return (
    <Card>
      <CardHeader>
        <CardTitle>Form Title</CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="space-y-2">
          <Label htmlFor="field1">Field 1 Label</Label>
          <Input
            id="field1"
            type="text"
            placeholder="Enter value"
            value={field1}
            onChange={(e) => setField1(e.target.value)}
            disabled={isLoading}
          />
          {errors.field1 && <p className="text-xs text-red-500">{errors.field1}</p>}
        </div>

        <div className="space-y-2">
          <Label htmlFor="field2">Field 2 Label</Label>
          <Input
            id="field2"
            type="number"
            placeholder="0"
            value={field2}
            onChange={(e) => setField2(e.target.value)}
            disabled={isLoading}
          />
          {errors.field2 && <p className="text-xs text-red-500">{errors.field2}</p>}
        </div>
      </CardContent>
      <CardFooter>
        <Button onClick={handleSubmit} disabled={isLoading} className="w-full">
          {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
          Submit
        </Button>
      </CardFooter>
    </Card>
  );
}

With List Management (Optional)

For forms that add/remove items from a list:

'use client';

import { useState } from 'react';
import { Trash2, Plus } from 'lucide-react';

export function FormWithList() {
  const [items, setItems] = useState<any[]>([]);
  const [currentItem, setCurrentItem] = useState('');

  const addItem = () => {
    if (!currentItem.trim()) {
      toast.error('Item cannot be empty');
      return;
    }
    setItems([...items, { id: Date.now(), name: currentItem }]);
    setCurrentItem('');
    toast.success(`Added ${currentItem}`);
  };

  const removeItem = (index: number) => {
    const itemName = items[index].name;
    setItems(items.filter((_, i) => i !== index));
    toast.info(`Removed ${itemName}`);
  };

  return (
    <>
      <div className="flex gap-2">
        <Input
          value={currentItem}
          onChange={(e) => setCurrentItem(e.target.value)}
          placeholder="Enter item"
        />
        <Button onClick={addItem} variant="outline" size="sm">
          <Plus className="h-4 w-4" />
        </Button>
      </div>

      <div className="space-y-2 mt-4">
        {items.map((item, index) => (
          <div key={item.id} className="flex items-center justify-between bg-muted p-2 rounded">
            <span>{item.name}</span>
            <Button
              onClick={() => removeItem(index)}
              variant="ghost"
              size="sm"
            >
              <Trash2 className="h-4 w-4 text-destructive" />
            </Button>
          </div>
        ))}
      </div>
    </>
  );
}

Key Conventions

  • Use 'use client' directive at top
  • useState for all form fields
  • useHealthStore hook for store actions
  • Validation function before submit
  • Try-catch-toast error handling
  • Set isLoading state on button during submission
  • Clear form fields after successful submit
  • Use shadcn/ui components (Input, Button, Label, etc.)
  • Show validation errors below each field
  • Disable inputs while loading
  • Use Loader2 icon for loading state
  • Toast notifications for all user actions

Steps

  1. Ask user for form name, fields, store action, and list management
  2. Create file: src/components/forms/{FormName}.tsx
  3. Generate component with Card wrapper
  4. Add useState for each field
  5. Add useHealthStore hook integration
  6. Add validation function
  7. Add handleSubmit with try-catch-toast
  8. Add form fields with shadcn/ui components
  9. Add list management if requested
  10. Format with Prettier

Implementation Checklist

  • Component exports correctly
  • 'use client' directive present
  • useState for all fields
  • useHealthStore hook imported
  • Validation function implemented
  • Error state management
  • handleSubmit with try-catch
  • Toast notifications added
  • isLoading state used
  • shadcn/ui components used
  • Error messages displayed
  • List management (if applicable)