Skip to main content

Creating New Components

1. Creating a Base UI Component

Step 1: Create component file in src/components/ui/

// src/components/ui/my-component.tsx
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/cn";

const myComponentVariants = cva(
// Base classes
"base-classes-here",
{
variants: {
variant: {
default: "default-variant-classes",
secondary: "secondary-variant-classes",
},
size: {
sm: "small-size-classes",
md: "medium-size-classes",
lg: "large-size-classes",
},
},
defaultVariants: {
variant: "default",
size: "md",
},
}
);

export interface MyComponentProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof myComponentVariants> {
// Custom props
customProp?: string;
}

const MyComponent = React.forwardRef<HTMLDivElement, MyComponentProps>(
({ className, variant, size, customProp, ...props }, ref) => {
return (
<div
ref={ref}
className={cn(myComponentVariants({ variant, size, className }))}
{...props}
>
{/* Component content */}
</div>
);
}
);

MyComponent.displayName = "MyComponent";

export { MyComponent, myComponentVariants };

Step 2: Use the component

import { MyComponent } from "@/components/ui/my-component";

export function MyPage() {
return (
<MyComponent variant="default" size="md">
Content
</MyComponent>
);
}

2. Creating a Form Component

Step 1: Create form component in src/components/forms/formComponents/

// src/components/forms/formComponents/MyFieldForm.tsx
"use client";
import { useFormContext } from "react-hook-form";
import {
FormField,
FormItem,
FormLabel,
FormControl,
FormMessage,
FormDescription,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";

export interface MyFieldFormProps {
name: string;
label: string;
placeholder?: string;
description?: string;
required?: boolean;
className?: string;
}

export default function MyFieldForm({
name,
label,
placeholder,
description,
required,
className,
}: MyFieldFormProps) {
const { control } = useFormContext();

return (
<FormField
control={control}
name={name}
render={({ field }) => (
<FormItem className={className}>
<FormLabel>
{label} {required && <span className="text-red-500">*</span>}
</FormLabel>
<FormControl>
<Input
{...field}
placeholder={placeholder}
value={field.value || ""}
/>
</FormControl>
{description && <FormDescription>{description}</FormDescription>}
<FormMessage />
</FormItem>
)}
/>
);
}

Step 2: Use in a form

import { useForm } from "react-hook-form";
import { Form } from "@/components/ui/form";
import MyFieldForm from "@/components/forms/formComponents/MyFieldForm";

export function MyForm() {
const form = useForm();

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<MyFieldForm
name="myField"
label="My Field"
placeholder="Enter value"
required
/>
</form>
</Form>
);
}

3. Creating a Feature Component

Step 1: Create component in feature directory

// src/components/campaign/campaign-card.tsx
"use client";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { CampaignType } from "@/types/campaign";

export interface CampaignCardProps {
campaign: CampaignType;
onEdit?: (id: string) => void;
}

export function CampaignCard({ campaign, onEdit }: CampaignCardProps) {
return (
<Card>
<CardHeader>
<CardTitle>{campaign.campaignName}</CardTitle>
</CardHeader>
<CardContent>
<p>{campaign.jurisdictionName}</p>
{onEdit && (
<Button onClick={() => onEdit(campaign.id)}>Edit</Button>
)}
</CardContent>
</Card>
);
}