Skip to content
React

How to Build React Components with AI

Generate a properly typed React component with an AI tool, then extend it with props, state and a test the right way.

Beginner9 min read

Goal

AI tools are genuinely good at generating a first-pass React component — the risk is in stopping there. This tutorial builds a `PricingCard` component from a prompt, then walks through the parts a beginner usually skips: proper typing, state, and a test.

Step 1: Describe the component with real requirements

Prompt
Create a PricingCard component in components/pricing-card.tsx. Props:
name (string), price (number), features (string array), and an optional
highlighted (boolean) that adds an accent border. Use TypeScript with an
explicit props type, not inline props. Style with Tailwind CSS.

Step 2: Check the generated component for a real type, not `any`

This is the most common corner AI tools cut under time pressure — verify the props are explicitly typed rather than left implicit or typed as `any`.

components/pricing-card.tsx
type PricingCardProps = {
  name: string
  price: number
  features: string[]
  highlighted?: boolean
}

export function PricingCard({ name, price, features, highlighted }: PricingCardProps) {
  return (
    <div
      className={`rounded-2xl border p-6 ${highlighted ? "border-indigo-500 shadow-lg" : "border-slate-200"}`}
    >
      <h3 className="text-lg font-semibold">{name}</h3>
      <p className="mt-2 text-3xl font-bold">
        ${price}
        <span className="text-sm font-normal">/mo</span>
      </p>
      <ul className="mt-4 space-y-2 text-sm">
        {features.map((feature) => (
          <li key={feature}>✓ {feature}</li>
        ))}
      </ul>
    </div>
  )
}

Step 3: Add real interactivity, not a static prop

Ask for the actual behavior you need — a monthly/annual toggle — rather than accepting a component that only ever shows one static price.

components/pricing-toggle.tsx
"use client"

import { useState } from "react"

export function PricingToggle({ monthlyPrice, annualPrice }: { monthlyPrice: number; annualPrice: number }) {
  const [annual, setAnnual] = useState(false)

  return (
    <div>
      <button onClick={() => setAnnual((prev) => !prev)}>
        {annual ? "Show monthly" : "Show annual"}
      </button>
      <p className="mt-2 text-3xl font-bold">${annual ? annualPrice : monthlyPrice}</p>
    </div>
  )
}

Step 4: Watch for these common AI mistakes in React output

  • Using array index as a `key` instead of a stable, unique value — breaks on reordering
  • Missing `"use client"` on a component that uses `useState` or `useEffect` inside the App Router
  • Adding a `useEffect` where a plain derived value or event handler would do — a common AI over-reach
  • Re-fetching or re-computing something on every render instead of memoizing it appropriately

Step 5: Write a test instead of only eyeballing it

components/pricing-card.test.tsx
import { render, screen } from "@testing-library/react"
import { describe, expect, it } from "vitest"
import { PricingCard } from "./pricing-card"

describe("PricingCard", () => {
  it("renders the name, price and all features", () => {
    render(<PricingCard name="Pro" price={29} features={["Unlimited projects", "Priority support"]} />)
    expect(screen.getByText("Pro")).toBeInTheDocument()
    expect(screen.getByText("Unlimited projects")).toBeInTheDocument()
  })
})

Where to go next

Once you're comfortable generating and reviewing individual components, the natural next step is wiring them into a real page with data — see /tutorials/connect-api-to-ai-generated-app for how to do that safely.

Ready to build the next one?

Browse the full tutorial library or grab a ready-made prompt for your next step.