12 промтов для создания React/Next.js приложений

Когда нужно быстро набросать компонент или страницу на React и Next.js, правильный промт экономит часы. В этой подборке — 12 проверенных запросов, которые я сам использую в ежедневной фронтенд-разработке. Каждый сопровождается примером кода и пояснением, почему он работает.

1. Функциональный компонент с TypeScript

Промт: «Напиши React-компонент на TypeScript, который принимает пропсы title: string и items: string[], и рендерит список с заголовком. Добавь типизацию пропсов через интерфейс.»

Пример:

interface ListProps {
  title: string;
  items: string[];
}

const List: React.FC<ListProps> = ({ title, items }) => (
  <div>
    <h2>{title}</h2>
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  </div>
);

export default List;

Почему работает: Запрос явно указывает типы и структуру, AI не гадает — результат сразу готов к вставке.

2. Компонент с хуками useState и useEffect

Промт: «Создай компонент Timer, который показывает секунды, запускает таймер при монтировании и очищает при размонтировании. Используй useState и useEffect, типизируй состояние как number.»

Пример:

import { useState, useEffect } from 'react';

const Timer: React.FC = () => {
  const [seconds, setSeconds] = useState<number>(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((prev) => prev + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return <div>Прошло: {seconds} сек</div>;
};

Почему работает: Упомянуты жизненный цикл и очистка — AI корректно добавляет return в эффект.

3. Серверный компонент в Next.js App Router

Промт: «Напиши серверный компонент для Next.js App Router, который получает список пользователей из JSONPlaceholder (https://jsonplaceholder.typicode.com/users) и рендерит карточки с именем и email. Компонент должен быть асинхронным и выполняться на сервере.»

Пример:

// app/users/page.tsx
interface User {
  id: number;
  name: string;
  email: string;
}

async function UsersPage() {
  const res = await fetch('https://jsonplaceholder.typicode.com/users');
  const users: User[] = await res.json();

  return (
    <div>
      {users.map((user) => (
        <div key={user.id}>
          <h3>{user.name}</h3>
          <p>{user.email}</p>
        </div>
      ))}
    </div>
  );
}

export default UsersPage;

Почему работает: Явное указание на серверный рендеринг и источник данных — AI генерирует async-компонент без лишних клиентских хуков.

4. Клиентский компонент с 'use client'

Промт: «Создай клиентский компонент в Next.js App Router, который использует useState для переключения темы (светлая/тёмная). Добавь директиву 'use client' в начале файла.»

Пример:

'use client';
import { useState } from 'react';

const ThemeToggle: React.FC = () => {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Текущая тема: {theme}
    </button>
  );
};

5. Страница Pages Router с getStaticProps

Промт: «Напиши страницу Next.js (Pages Router), которая получает список постов через getStaticProps из JSONPlaceholder и передаёт их в компонент. Добавь типизацию пропсов.»

Пример:

import { GetStaticProps } from 'next';

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

interface Props {
  posts: Post[];
}

const Posts: React.FC<Props> = ({ posts }) => (
  <ul>
    {posts.map((post) => (
      <li key={post.id}>{post.title}</li>
    ))}
  </ul>
);

export const getStaticProps: GetStaticProps = async () => {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts');
  const posts: Post[] = await res.json();
  return { props: { posts } };
};

export default Posts;

6. Кастомный хук для работы с API

Промт: «Создай кастомный хук useFetch, который принимает URL и возвращает { data, loading, error }. Используй дженерик для типизации ответа. Добавь отмену запроса через AbortController.»

Пример:

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: T) => setData(data))
      .catch((err) => {
        if (err.name !== 'AbortError') setError(err.message);
      })
      .finally(() => setLoading(false));

    return () => controller.abort();
  }, [url]);

  return { data, loading, error };
}

7. Компонент с Context API

Промт: «Создай контекст авторизации AuthContext с состоянием { user, login, logout }. Реализуй провайдер и хук useAuth. Типизируй контекст.»

Пример (упрощённо):

import { createContext, useContext, useState, ReactNode } from 'react';

interface AuthContextType {
  user: string | null;
  login: (name: string) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  const [user, setUser] = useState<string | null>(null);
  const login = (name: string) => setUser(name);
  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
};

8. Форма с валидацией (React Hook Form)

Промт: «Создай форму регистрации с полями: email (обязательно, формат email), password (минимум 6 символов). Используй react-hook-form и yup для валидации. Выводи ошибки под каждым полем.»

Пример:

import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

const schema = yup.object({
  email: yup.string().email().required(),
  password: yup.string().min(6).required(),
});

type FormData = yup.InferType<typeof schema>;

const RegistrationForm: React.FC = () => {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
    resolver: yupResolver(schema),
  });

  const onSubmit = (data: FormData) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} placeholder='Email' />
      <p>{errors.email?.message}</p>
      <input type='password' {...register('password')} placeholder='Пароль' />
      <p>{errors.password?.message}</p>
      <button type='submit'>Зарегистрироваться</button>
    </form>
  );
};

9. Анимация с Framer Motion

Промт: «Напиши компонент, который анимирует появление блока сдвигом слева с opacity при монтировании. Используй framer-motion, вариант 'hidden' и 'visible'.»

Пример:

import { motion } from 'framer-motion';

const AnimatedBlock: React.FC = () => (
  <motion.div
    initial={{ opacity: 0, x: -100 }}
    animate={{ opacity: 1, x: 0 }}
    transition={{ duration: 0.5 }}
  >
    Привет, анимация!
  </motion.div>
);

10. Таблица с сортировкой

Промт: «Создай таблицу с колонками Name и Age. Данные — массив объектов { name: string; age: number }. Добавь возможность сортировки по возрасту по клику на заголовок. Используй useState.»

Пример (упрощённо):

import { useState } from 'react';

interface Person { name: string; age: number }

type SortDirection = 'asc' | 'desc';

const Table: React.FC<{ data: Person[] }> = ({ data }) => {
  const [sortDir, setSortDir] = useState<SortDirection>('asc');

  const sorted = [...data].sort((a, b) =>
    sortDir === 'asc' ? a.age - b.age : b.age - a.age
  );

  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th onClick={() => setSortDir(sortDir === 'asc' ? 'desc' : 'asc')}>
            Age {sortDir === 'asc' ? '↑' : '↓'}
          </th>
        </tr>
      </thead>
      <tbody>
        {sorted.map((p, i) => (
          <tr key={i}><td>{p.name}</td><td>{p.age}</td></tr>
        ))}
      </tbody>
    </table>
  );
};

11. Модальное окно с порталом

Промт: «Напиши компонент Modal, который рендерится через React Portal в body. Принимает isOpen, onClose и children. Добавь обработку клика по затемнённому фону для закрытия.»

Пример:

import { useEffect } from 'react';
import { createPortal } from 'react-dom';

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

const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children }) => {
  useEffect(() => {
    const handleEsc = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', handleEsc);
    return () => window.removeEventListener('keydown', handleEsc);
  }, [onClose]);

  if (!isOpen) return null;

  return createPortal(
    <div
      style={{
        position: 'fixed', inset: 0, display: 'flex',
        alignItems: 'center', justifyContent: 'center',
        backgroundColor: 'rgba(0,0,0,0.5)'
      }}
      onClick={onClose}
    >
      <div onClick={(e) => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
};

12. Компонент с lazy loading и Suspense

Промт: «Создай страницу, которая лениво загружает тяжёлый компонент через React.lazy и оборачивает его в Suspense с fallback (спиннер). Используй React 18+ и Next.js App Router.»

Пример:

import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

const LazyPage: React.FC = () => (
  <Suspense fallback={<div>Загрузка...</div>}>
    <HeavyComponent />
  </Suspense>
);

Эти 12 промтов покрывают 80% типовых задач фронтендера. Сохраните их в шпаргалку — и AI станет вашим ассистентом, а не просто игрушкой. Пробуйте, адаптируйте под свои проекты и пишите более сложные сценарии, комбинируя несколько промтов в одном.

← Все статьи

Комментарии