Introduction
If you’re a frontend developer working with React or Next.js, you know that repetitive boilerplate and complex state management can eat up hours. AI prompts can accelerate your workflow—from generating components to writing tests. I’ve compiled 15 battle-tested prompts I use daily. Each includes a real example and code output. No fluff, just practical value.
1. Generate a Functional Component with TypeScript
Prompt: "Create a React functional component in TypeScript that displays a user card. It should accept name, email, and avatarUrl as props. Use Tailwind CSS for styling."
Example output:
import React from 'react';
interface UserCardProps {
name: string;
email: string;
avatarUrl: string;
}
const UserCard: React.FC<UserCardProps> = ({ name, email, avatarUrl }) => {
return (
<div className="flex items-center p-4 bg-white rounded-lg shadow-md">
<img src={avatarUrl} alt={name} className="w-12 h-12 rounded-full" />
<div className="ml-4">
<h2 className="text-lg font-semibold">{name}</h2>
<p className="text-gray-500">{email}</p>
</div>
</div>
);
};
export default UserCard;
Why it works: This prompt specifies framework (React), language (TypeScript), props, and styling library. It reduces back-and-forth and produces production-ready code.
2. Create a Next.js API Route
Prompt: "Write a Next.js API route for a blog app that fetches a single post by ID from a mock database. Use TypeScript and return a 404 if not found."
Example output:
import { NextApiRequest, NextApiResponse } from 'next';
const posts = [
{ id: 1, title: 'Hello World', content: 'This is my first post.' },
{ id: 2, title: 'Second Post', content: 'Another post here.' },
];
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { id } = req.query;
const post = posts.find((p) => p.id === Number(id));
if (!post) {
return res.status(404).json({ message: 'Post not found' });
}
res.status(200).json(post);
}
Why it works: The prompt defines the route type (API), data source (mock), and error handling. Perfect for prototyping.
3. Generate a Custom Hook
Prompt: "Create a custom React hook useLocalStorage that syncs state with localStorage. It should accept a key and default value, and return the value and a setter."
Example output:
import { useState, useEffect } from 'react';
function useLocalStorage<T>(key: string, defaultValue: T): [T, (value: T) => void] {
const [value, setValue] = useState<T>(() => {
if (typeof window !== 'undefined') {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : defaultValue;
}
return defaultValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
export default useLocalStorage;
Why it works: Hooks are reusable logic. This prompt includes TypeScript generics, SSR safety, and state persistence.
4. Build a Form with Validation
Prompt: "Generate a React form for user registration with fields: name, email, password. Use react-hook-form and zod for validation. Show error messages below each field."
Example output:
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
const schema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters'),
email: z.string().email('Invalid email'),
password: z.string().min(6, 'Password must be at least 6 characters'),
});
type FormData = z.infer<typeof schema>;
export default function RegisterForm() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<input {...register('name')} placeholder="Name" className="border p-2" />
{errors.name && <p className="text-red-500">{errors.name.message}</p>}
<input {...register('email')} placeholder="Email" className="border p-2" />
{errors.email && <p className="text-red-500">{errors.email.message}</p>}
<input type="password" {...register('password')} placeholder="Password" className="border p-2" />
{errors.password && <p className="text-red-500">{errors.password.message}</p>}
<button type="submit" className="bg-blue-500 text-white p-2">Register</button>
</form>
);
}
Why it works: Combines two popular libraries (react-hook-form, zod) for robust validation. The prompt specifies UI requirements (error messages).
5. Generate a Next.js Page with Server-Side Props
Prompt: "Write a Next.js page that fetches a list of users from an API on the server side and renders them in a table. Use getServerSideProps."
Example output:
import { GetServerSideProps } from 'next';
interface User {
id: number;
name: string;
email: string;
}
interface UsersPageProps {
users: User[];
}
export const getServerSideProps: GetServerSideProps<UsersPageProps> = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const users: User[] = await res.json();
return { props: { users } };
};
export default function UsersPage({ users }: UsersPageProps) {
return (
<table className="min-w-full">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.email}</td>
</tr>
))}
</tbody>
</table>
);
}
Why it works: Specifies data fetching method (SSR), API source, and rendering format (table). Ready for SEO.
6. Create a Context Provider
Prompt: "Write a React context for a theme (light/dark). Include a provider component and a custom hook to consume it."
Example output:
import React, { createContext, useContext, useState, ReactNode } from 'react';
type Theme = 'light' | 'dark';
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const ThemeProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState<Theme>('light');
const toggleTheme = () => setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = (): ThemeContextType => {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
};
Why it works: Full context pattern with provider, consumer hook, and error handling. Reusable across any component.
7. Generate a Loading Skeleton Component
Prompt: "Create a React loading skeleton component for a card layout. It should animate with a shimmer effect using CSS."
Example output:
import React from 'react';
const SkeletonCard: React.FC = () => {
return (
<div className="animate-pulse bg-white p-4 rounded-lg shadow-md">
<div className="w-12 h-12 bg-gray-300 rounded-full mb-4"></div>
<div className="h-4 bg-gray-300 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-300 rounded w-1/2"></div>
</div>
);
};
export default SkeletonCard;
Why it works: Tailwind’s animate-pulse gives an instant shimmer effect. No extra CSS needed.
8. Write a Unit Test with Jest and React Testing Library
Prompt: "Write a Jest test for a UserCard component. It should check that the name and email render correctly."
Example output:
import { render, screen } from '@testing-library/react';
import UserCard from './UserCard';
test('renders user name and email', () => {
render(<UserCard name="John Doe" email="john@example.com" avatarUrl="https://via.placeholder.com/50" />);
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
});
Why it works: Focuses on one component with clear assertions. Follows RTL best practices.
9. Generate a Next.js Middleware
Prompt: "Write a Next.js middleware that redirects unauthenticated users to the login page. Assume a cookie named 'token'."
Example output:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: '/dashboard/:path*',
};
Why it works: Uses matcher for efficiency, handles cookie-based auth. Production-ready.
10. Create a Reusable Modal Component
Prompt: "Write a React modal component using portals. It should accept children and an onClose prop. Close on overlay click and Escape key."
Example output:
import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';
interface ModalProps {
children: React.ReactNode;
onClose: () => void;
}
const Modal: React.FC<ModalProps> = ({ children, onClose }) => {
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, [onClose]);
return ReactDOM.createPortal(
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" onClick={onClose}>
<div className="bg-white p-6 rounded-lg" onClick={(e) => e.stopPropagation()}>
{children}
</div>
</div>,
document.body
);
};
export default Modal;
Why it works: Handles accessibility (Escape), portal usage, and click-outside close.
11. Generate an Infinite Scroll Component
Prompt: "Create a React component that fetches more data when the user scrolls to the bottom. Use Intersection Observer."
Example output:
import { useEffect, useRef, useState } from 'react';
const InfiniteScroll: React.FC = () => {
const [items, setItems] = useState<string[]>([]);
const [page, setPage] = useState(1);
const loaderRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
setPage((prev) => prev + 1);
}
});
if (loaderRef.current) observer.observe(loaderRef.current);
return () => observer.disconnect();
}, []);
useEffect(() => {
fetch(`https://api.example.com/items?page=${page}`)
.then((res) => res.json())
.then((newItems) => setItems((prev) => [...prev, ...newItems]));
}, [page]);
return (
<div>
{items.map((item, i) => <div key={i}>{item}</div>)}
<div ref={loaderRef}>Loading more...</div>
</div>
);
};
export default InfiniteScroll;
Why it works: Uses Intersection Observer for performance, pagination logic is built-in.
12. Write a Debounced Search Hook
Prompt: "Create a custom hook useDebounce that delays updating a value by a given delay in milliseconds."
Example output:
import { useState, useEffect } from 'react';
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;
}
export default useDebounce;
Why it works: Clean, generic, and essential for search inputs to reduce API calls.
13. Generate an Error Boundary
Prompt: "Write a React error boundary component that catches errors and displays a fallback UI with a retry button."
Example output:
import React, { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(_: Error): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Error caught:', error, errorInfo);
}
handleRetry = () => {
this.setState({ hasError: false });
};
render() {
if (this.state.hasError) {
return (
<div className="text-center">
<h2>Something went wrong.</h2>
<button onClick={this.handleRetry} className="bg-blue-500 text-white p-2">Try again</button>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Why it works: Implements React error boundary pattern, logs errors, and provides user recovery.
14. Create a Dynamic Form with Multiple Fields
Prompt: "Generate a React component that allows adding/removing input fields dynamically. Each field has a name and value."
Example output:
import { useState } from 'react';
interface Field {
id: number;
name: string;
value: string;
}
const DynamicForm: React.FC = () => {
const [fields, setFields] = useState<Field[]>([{ id: 1, name: '', value: '' }]);
const addField = () => {
setFields([...fields, { id: Date.now(), name: '', value: '' }]);
};
const removeField = (id: number) => {
setFields(fields.filter((f) => f.id !== id));
};
const updateField = (id: number, key: 'name' | 'value', val: string) => {
setFields(fields.map((f) => (f.id === id ? { ...f, [key]: val } : f)));
};
return (
<div>
{fields.map((field) => (
<div key={field.id} className="flex gap-2 mb-2">
<input placeholder="Name" value={field.name} onChange={(e) => updateField(field.id, 'name', e.target.value)} className="border p-1" />
<input placeholder="Value" value={field.value} onChange={(e) => updateField(field.id, 'value', e.target.value)} className="border p-1" />
<button onClick={() => removeField(field.id)} className="bg-red-500 text-white p-1">Remove</button>
</div>
))}
<button onClick={addField} className="bg-green-500 text-white p-1">Add Field</button>
</div>
);
};
export default DynamicForm;
Why it works: Manages dynamic fields with unique IDs, supports add/remove/update.
15. Generate a Next.js Static Page with getStaticProps
Prompt: "Write a Next.js page that fetches data at build time from a CMS API and renders it as a static page."
Example output:
import { GetStaticProps } from 'next';
interface Post {
id: number;
title: string;
body: string;
}
interface PostsPageProps {
posts: Post[];
}
export const getStaticProps: GetStaticProps<PostsPageProps> = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts: Post[] = await res.json();
return { props: { posts }, revalidate: 3600 };
};
export default function PostsPage({ posts }: PostsPageProps) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</li>
))}
</ul>
);
}
Why it works: Uses Incremental Static Regeneration (revalidate) for performance.
Conclusion
These 15 prompts cover the most common tasks—components, hooks, API routes, forms, tests, and more. Copy them into your AI tool, tweak the details, and watch your productivity spike. Try one today on your current project.
For more resources, check the React documentation and Next.js docs.
Comments