10 Prompts for Building React/Next.js Apps Like a Pro

10 Prompts for Building React/Next.js Apps Like a Pro

Building modern frontend applications with React and Next.js is a rewarding yet complex task. Whether you are a seasoned developer or just starting out, crafting the right components, handling state, and optimizing performance can eat up hours of your day. That's where carefully designed prompts come in — they help you generate reusable React components, scaffold Next.js pages, and even write unit tests with minimal friction.

In this guide, you will find 10 ready-to-use prompts that cover the most common scenarios in React/Next.js development. Each prompt is explained with its purpose, a concrete usage example, and the expected output. Copy, paste, and adapt them to your workflow — no fluff, just practical value.

1. Generate a Reusable Functional Component with TypeScript

Task: Create a typed, reusable React component with proper props interface and default values.

Prompt:

Generate a reusable React functional component in TypeScript that renders a user profile card. It should accept `name`, `email`, `avatarUrl`, and `role` as props. Use default values for `role` ("User") and `avatarUrl` (a placeholder image). Include a styled wrapper using CSS Modules. Add a brief docstring explaining the component.

Usage example:
Paste the prompt into an AI assistant. The output will be a complete ProfileCard.tsx file with an interface, default props, and module-scoped styles.

Expected output:

import styles from './ProfileCard.module.css';

interface ProfileCardProps {
  name: string;
  email: string;
  avatarUrl?: string;
  role?: string;
}

/** Displays a user profile card with avatar, name, email, and role. */
const ProfileCard: React.FC<ProfileCardProps> = ({
  name,
  email,
  avatarUrl = '/placeholder-avatar.png',
  role = 'User',
}) => {
  return (
    <div className={styles.card}>
      <img src={avatarUrl} alt={`${name}'s avatar`} className={styles.avatar} />
      <h2 className={styles.name}>{name}</h2>
      <p className={styles.email}>{email}</p>
      <span className={styles.role}>{role}</span>
    </div>
  );
};

export default ProfileCard;

2. Scaffold a Next.js Page with Dynamic Routes and getStaticProps

Task: Quickly generate a Next.js page that uses dynamic routing and server-side data fetching.

Prompt:

Create a Next.js page for a blog post using dynamic route `[id]`. Use `getStaticPaths` to generate paths for three sample posts (id: 1, 2, 3) and `getStaticProps` to fetch post data from a local JSON file. The page should display title, body, and a back link. Use TypeScript and Next.js 14 App Router (if available) or Pages Router.

Usage example:
Use this prompt to bootstrap a blog post page in seconds. The AI will generate both the page component and the data file.

Expected output (simplified):

// pages/posts/[id].tsx
import { GetStaticPaths, GetStaticProps } from 'next';
import posts from '../../data/posts.json';

interface Post {
  id: number;
  title: string;
  body: string;
}

export const getStaticPaths: GetStaticPaths = async () => {
  const paths = posts.map((post) => ({ params: { id: post.id.toString() } }));
  return { paths, fallback: false };
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const post = posts.find((p) => p.id.toString() === params?.id);
  return { props: { post } };
};

const PostPage: React.FC<{ post: Post }> = ({ post }) => (
  <div>
    <h1>{post.title}</h1>
    <p>{post.body}</p>
    <a href="/">Back to home</a>
  </div>
);

export default PostPage;

3. Create a Custom Hook for API Data Fetching

Task: Build a reusable custom hook that handles loading, error, and data states from an API call.

Prompt:

Write a custom React hook called `useFetch` that accepts a URL string and returns an object with `data`, `loading`, and `error` states. Use `useEffect` with `AbortController` to cancel requests on unmount. Use TypeScript generics so the hook can work with any data type. Provide a usage example with a mock API.

Usage example:
Drop this hook into any component that needs to fetch data. It handles edge cases like race conditions and cleanup.

Expected output:

import { useState, useEffect } from 'react';

function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);
    fetch(url, { signal: controller.signal })
      .then((res) => res.json())
      .then((data) => setData(data))
      .catch((err) => {
        if (err.name !== 'AbortError') setError(err.message);
      })
      .finally(() => setLoading(false));
    return () => controller.abort();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

4. Build a Controlled Form with Validation

Task: Generate a form component with multiple input fields and client-side validation.

Prompt:

Create a React component that renders a registration form with fields: username, email, password, and confirm password. Implement controlled inputs using `useState`. Add validation rules: username min 3 chars, valid email format, password min 8 chars, passwords must match. Show inline error messages. On submit, log the data to console. Use TypeScript.

Usage example:
Use this prompt to quickly scaffold a sign-up form. The AI will output a complete component with validation logic.

Expected output (snippet):

const RegistrationForm: React.FC = () => {
  const [form, setForm] = useState({ username: '', email: '', password: '', confirmPassword: '' });
  const [errors, setErrors] = useState<Record<string, string>>({});

  const validate = () => {
    const newErrors: Record<string, string> = {};
    if (form.username.length < 3) newErrors.username = 'Username must be at least 3 characters';
    if (!/\S+@\S+\.\S+/.test(form.email)) newErrors.email = 'Invalid email';
    if (form.password.length < 8) newErrors.password = 'Password must be at least 8 characters';
    if (form.password !== form.confirmPassword) newErrors.confirmPassword = 'Passwords do not match';
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (validate()) console.log('Form data:', form);
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* input fields with error messages */}
    </form>
  );
};

5. Implement a Debounced Search Component

Task: Create a search input that delays API calls until the user stops typing.

Prompt:

Write a React component called `DebouncedSearch` that includes an input field and a list of results. Use a custom `useDebounce` hook (value and delay) to debounce the input value by 300ms. When the debounced value changes, simulate an API call (use `setTimeout` to return mock data after 500ms). Display a loading indicator during the fetch. Use TypeScript.

Usage example:
Perfect for autocomplete or search features. The prompt generates both the hook and the component.

Expected output:

function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState(value);
  useEffect(() => {
    const handler = setTimeout(() => setDebouncedValue(value), delay);
    return () => clearTimeout(handler);
  }, [value, delay]);
  return debouncedValue;
}

const DebouncedSearch: React.FC = () => {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState<string[]>([]);
  const [loading, setLoading] = useState(false);
  const debouncedQuery = useDebounce(query, 300);

  useEffect(() => {
    if (!debouncedQuery) return;
    setLoading(true);
    setTimeout(() => {
      setResults(['Result 1', 'Result 2', 'Result 3']);
      setLoading(false);
    }, 500);
  }, [debouncedQuery]);

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      {loading && <p>Loading...</p>}
      <ul>{results.map((r) => <li key={r}>{r}</li>)}</ul>
    </div>
  );
};

6. Generate a Responsive Navigation Bar with Next.js Link

Task: Build a mobile-friendly navbar that uses Next.js Link for client-side navigation.

Prompt:

Create a responsive navigation bar component for a Next.js app using the App Router. It should include links: Home, About, Blog, Contact. Use a hamburger menu for mobile screens (breakpoint 768px). Style with CSS Modules. Use `useState` to toggle the menu. Use the `Link` component from `next/link`. Add an active link state using `usePathname` from `next/navigation`.

Usage example:
Copy the generated code into your layout file. The prompt handles responsiveness and routing.

Expected output (structure):

'use client';
import { useState } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import styles from './Navbar.module.css';

const Navbar: React.FC = () => {
  const [isOpen, setIsOpen] = useState(false);
  const pathname = usePathname();

  const links = [
    { href: '/', label: 'Home' },
    { href: '/about', label: 'About' },
    { href: '/blog', label: 'Blog' },
    { href: '/contact', label: 'Contact' },
  ];

  return (
    <nav className={styles.nav}>
      <button onClick={() => setIsOpen(!isOpen)} className={styles.hamburger}>
        
      </button>
      <ul className={`${styles.menu} ${isOpen ? styles.open : ''}`}>
        {links.map((link) => (
          <li key={link.href} className={pathname === link.href ? styles.active : ''}>
            <Link href={link.href}>{link.label}</Link>
          </li>
        ))}
      </ul>
    </nav>
  );
};

export default Navbar;

7. Write a Unit Test for a React Component with Jest and Testing Library

Task: Generate a test file for an existing component.

Prompt:

Write a unit test for the following React component using Jest and @testing-library/react. The component is a `Counter` that displays a count and has increment/decrement buttons. Test that: the initial count is 0, clicking increment increases count by 1, clicking decrement decreases count by 1, and the buttons are disabled when count reaches 0 (decrement) or 10 (increment). Use `render`, `screen`, and `fireEvent`.

Usage example:
Paste the prompt along with your component code. The AI returns a complete Counter.test.tsx file.

Expected output:

import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

describe('Counter', () => {
  it('renders with initial count 0', () => {
    render(<Counter />);
    expect(screen.getByText('Count: 0')).toBeInTheDocument();
  });

  it('increments count on button click', () => {
    render(<Counter />);
    fireEvent.click(screen.getByText('+'));
    expect(screen.getByText('Count: 1')).toBeInTheDocument();
  });

  it('decrements count on button click', () => {
    render(<Counter />);
    fireEvent.click(screen.getByText('+'));
    fireEvent.click(screen.getByText('-'));
    expect(screen.getByText('Count: 0')).toBeInTheDocument();
  });

  it('disables decrement button at 0', () => {
    render(<Counter />);
    expect(screen.getByText('-')).toBeDisabled();
  });
});

8. Optimize a List with React.memo and useCallback

Task: Improve performance of a large list component.

Prompt:

Create a React component that renders a list of 1000 items. Each item shows a name and a delete button. Use `React.memo` to prevent unnecessary re-renders of list items. Use `useCallback` for the delete handler. Include a text input to filter items by name. Use TypeScript. Explain why memo and useCallback are used here.

Usage example:
Use this prompt to learn performance optimization. The generated code demonstrates best practices.

Expected output (snippet):

const ListItem: React.FC<{ name: string; onDelete: (name: string) => void }> = React.memo(({ name, onDelete }) => {
  console.log('Rendering:', name);
  return (
    <li>
      {name} <button onClick={() => onDelete(name)}>Delete</button>
    </li>
  );
});

const OptimizedList: React.FC = () => {
  const [items, setItems] = useState(Array.from({ length: 1000 }, (_, i) => `Item ${i}`));
  const [filter, setFilter] = useState('');

  const handleDelete = useCallback((name: string) => {
    setItems((prev) => prev.filter((item) => item !== name));
  }, []);

  const filteredItems = items.filter((item) => item.toLowerCase().includes(filter.toLowerCase()));

  return (
    <div>
      <input value={filter} onChange={(e) => setFilter(e.target.value)} placeholder="Filter..." />
      <ul>{filteredItems.map((item) => <ListItem key={item} name={item} onDelete={handleDelete} />)}</ul>
    </div>
  );
};

9. Create a Modal Component with Portal

Task: Build a modal that renders outside the parent DOM hierarchy using React Portal.

Prompt:

Create a reusable Modal component using ReactDOM.createPortal. The modal should: accept `isOpen`, `onClose`, and `children` props; render into a div with id "modal-root" (assume it exists in the HTML); include an overlay that closes the modal on click; and trap focus inside the modal (basic implementation). Use TypeScript and CSS Modules.

Usage example:
Use this prompt to get a production-ready modal. The AI handles portal logic and basic accessibility.

Expected output:

import { useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';
import styles from './Modal.module.css';

interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  children: React.ReactNode;
}

const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children }) => {
  const modalRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (isOpen && modalRef.current) {
      modalRef.current.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return createPortal(
    <div className={styles.overlay} onClick={onClose}>
      <div className={styles.modal} ref={modalRef} onClick={(e) => e.stopPropagation()} tabIndex={-1}>
        {children}
        <button onClick={onClose}>Close</button>
      </div>
    </div>,
    document.getElementById('modal-root')!
  );
};

export default Modal;

10. Generate a Next.js API Route with Middleware

Task: Create a backend API endpoint in Next.js with authentication middleware.

Prompt:

Write a Next.js API route for the App Router (route.ts) that creates a new user. Use a middleware to check for a valid API key in the request headers. If the key is missing or invalid, return a 401 response. The route should accept a POST request with JSON body containing name and email. Validate that both fields are present. Use TypeScript.

Usage example:
Paste this prompt to scaffold a secure API endpoint. The AI includes the middleware and validation logic.

Expected output:

// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';

function validateApiKey(request: NextRequest) {
  const apiKey = request.headers.get('x-api-key');
  if (apiKey !== process.env.API_KEY) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }
  return null;
}

export async function POST(request: NextRequest) {
  const authError = validateApiKey(request);
  if (authError) return authError;

  const body = await request.json();
  if (!body.name || !body.email) {
    return NextResponse.json({ error: 'Name and email are required' }, { status: 400 });
  }

  // Simulate user creation
  return NextResponse.json({ message: 'User created', user: { name: body.name, email: body.email } }, { status: 201 });
}

Conclusion

These 10 prompts will save you hours of boilerplate code writing and help you focus on the unique logic of your application. From reusable components to API routes and testing, each prompt is designed to be drop-in ready for your React or Next.js projects. The key is to customize them — adjust the props, styling, or validation rules to fit your exact needs.

Start using these prompts today with any AI coding assistant. The more specific you are in your prompt, the better the output. Happy coding — and may your components always be reusable!

← All posts

Comments