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

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

React и Next.js остаются одними из самых популярных инструментов для фронтенд-разработки в 2026 году. По данным опроса State of JS 2025, React используют 87% респондентов, а Next.js — 63% среди фреймворков. Однако рутинные задачи — от написания типовых компонентов до настройки маршрутизации — отнимают время, которое можно потратить на архитектуру и бизнес-логику. Я собрал 10 проверенных промтов, которые использую ежедневно в коммерческих проектах. Каждый промт сопровождается примером использования и кодом.

1. Генерация функционального компонента с TypeScript и Storybook

Когда использовать: Создание нового UI-компонента для библиотеки или страницы.

Промт: «Сгенерируй React компонент на TypeScript с именем Button. Компонент должен принимать пропсы: variant (primary

| secondary), size (sm | md | lg), children (ReactNode), onClick (функция). Используй CSS modules для стилей. Создай файл .stories.tsx для Storybook с примерами всех вариантов. Экспортируй тип пропсов.»

Пример результата (Button.tsx):

import React from 'react';
import styles from './Button.module.css';

type ButtonVariant = 'primary' | 'secondary';
type ButtonSize = 'sm'

| 'md' | 'lg';

type ButtonProps = {
  variant?: ButtonVariant;
  size?: ButtonSize;
  children: React.ReactNode;
  onClick?: () => void;
};

export const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  size = 'md',
  children,
  onClick,
}) => {
  const className = `${styles.button} ${styles[variant]} ${styles[size]}`;
  return (
    <button className={className} onClick={onClick}>
      {children}
    </button>
  );
};

Button.stories.tsx:

import { Button } from './Button';

export default { component: Button };

export const Primary = { args: { variant: 'primary', children: 'Click me' } };
export const Secondary = { args: { variant: 'secondary', children: 'Cancel' } };

Совет: Уточните в промте, нужно ли использовать forwardRef для доступа к DOM-элементу — это часто требуется для форм.

2. Создание страницы Next.js с серверным рендерингом и SEO

Когда использовать: Добавление новой публичной страницы с динамическими данными.

Промт: «Создай страницу Next.js (App Router) для блога. Используй серверный компонент. Получи список постов из API https://jsonplaceholder.typicode.com/posts. Отобрази заголовки постов в виде ссылок на страницу /posts/[id]. Добавь метаданные (title, description) через generateMetadata. Используй CSS modules для стилей. Название файла: app/blog/page.tsx.»

Пример кода (app/blog/page.tsx):

import Link from 'next/link';
import styles from './page.module.css';

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

async function getPosts(): Promise<Post[]> {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts');
  if (!res.ok) throw new Error('Failed to fetch posts');
  return res.json();
}

export async function generateMetadata() {
  return { title: 'Блог', description: 'Список последних статей' };
}

export default async function BlogPage() {
  const posts = await getPosts();
  return (
    <div className={styles.container}>
      <h1>Блог</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <Link href={`/posts/${post.id}`}>{post.title}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

Важно: Для кеширования данных используйте параметр next: { revalidate: 60 } в fetch, чтобы страница обновлялась каждые 60 секунд.

3. Форма с валидацией через React Hook Form и Zod

Когда использовать: Регистрация, логин, любые формы с проверкой.

Промт: «Создай React компонент формы регистрации с полями: email, password (с подтверждением). Используй React Hook Form для управления состоянием и Zod для схемы валидации. Валидация: email — корректный email, password — минимум 8 символов, одна заглавная буква. Выводи ошибки под каждым полем. Стили — inline. Код на TypeScript.»

Пример кода:

import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';

const schema = z
  .object({
    email: z.string().email('Неверный email'),
    password: z
      .string()
      .min(8, 'Минимум 8 символов')
      .regex(/[A-Z]/, 'Нужна заглавная буква'),
    confirmPassword: z.string(),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Пароли не совпадают',
    path: ['confirmPassword'],
  });

type FormData = z.infer<typeof schema>;

export const RegisterForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>({ resolver: zodResolver(schema) });

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>Email</label>
        <input {...register('email')} />
        {errors.email && <p style={{ color: 'red' }}>{errors.email.message}</p>}
      </div>
      <div>
        <label>Пароль</label>
        <input type="password" {...register('password')} />
        {errors.password && <p style={{ color: 'red' }}>{errors.password.message}</p>}
      </div>
      <div>
        <label>Подтвердите пароль</label>
        <input type="password" {...register('confirmPassword')} />
        {errors.confirmPassword && (
          <p style={{ color: 'red' }}>{errors.confirmPassword.message}</p>
        )}
      </div>
      <button type="submit">Зарегистрироваться</button>
    </form>
  );
};

Совет: Для больших форм добавьте в промт «используй useFieldArray для динамических полей».

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

Когда использовать: Сохранение состояния между сессиями без сервера.

Промт: «Напиши кастомный React хук useLocalStorage, который принимает ключ (string) и начальное значение. Возвращает кортеж [value, setValue]. Хук должен синхронизироваться с localStorage, обрабатывать ошибки (например, если localStorage недоступен) и обновлять состояние при изменении ключа. Используй TypeScript с дженериками.»

Пример кода:

import { useState, useEffect, useCallback } from 'react';

export function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, (value: T | ((prev: T) => T)) => void] {
  const [storedValue, setStoredValue] = useState<T>(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? (JSON.parse(item) as T) : initialValue;
    } catch (error) {
      console.error('Error reading localStorage key “' + key + '”:', error);
      return initialValue;
    }
  });

  const setValue = useCallback(
    (value: T | ((prev: T) => T)) => {
      try {
        const valueToStore =
          value instanceof Function ? value(storedValue) : value;
        setStoredValue(valueToStore);
        window.localStorage.setItem(key, JSON.stringify(valueToStore));
      } catch (error) {
        console.error('Error setting localStorage key “' + key + '”:', error);
      }
    },
    [key, storedValue]
  );

  useEffect(() => {
    const handleStorageChange = (e: StorageEvent) => {
      if (e.key === key && e.newValue) {
        setStoredValue(JSON.parse(e.newValue));
      }
    };
    window.addEventListener('storage', handleStorageChange);
    return () => window.removeEventListener('storage', handleStorageChange);
  }, [key]);

  return [storedValue, setValue];
}

Применение: const [theme, setTheme] = useLocalStorage('theme', 'light');

5. Middleware для авторизации в Next.js

Когда использовать: Защита маршрутов от неавторизованных пользователей.

Промт: «Создай middleware для Next.js (файл middleware.ts в корне проекта). Проверяй наличие cookie с именем 'auth-token'. Если токена нет и путь начинается с /dashboard, /profile или /admin — перенаправляй на /login. Если токен есть и пользователь пытается зайти на /login — перенаправляй на /dashboard. Используй NextResponse. Оберни логику в try-catch.»

Пример кода (middleware.ts):

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const protectedPaths = ['/dashboard', '/profile', '/admin'];
const authPaths = ['/login'];

export function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')?.value;
  const path = request.nextUrl.pathname;

  try {
    if (protectedPaths.some((p) => path.startsWith(p)) && !token) {
      const loginUrl = new URL('/login', request.url);
      loginUrl.searchParams.set('redirect', path);
      return NextResponse.redirect(loginUrl);
    }

    if (authPaths.includes(path) && token) {
      return NextResponse.redirect(new URL('/dashboard', request.url));
    }

    return NextResponse.next();
  } catch (error) {
    console.error('Middleware error:', error);
    return NextResponse.redirect(new URL('/error', request.url));
  }
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*', '/login'],
};

Важно: Для продакшена используйте JWT-токены и проверку подписи на сервере; middleware — это клиентский уровень.

6. Анимация появления элементов при скролле

Когда использовать: Улучшение UX на лендингах.

Промт: «Создай React компонент FadeInOnScroll, который анимирует появление дочерних элементов при скролле. Используй Intersection Observer API. Компонент должен принимать пропс threshold (число от 0 до 1, по умолчанию 0.1) и duration (строку, по умолчанию '0.5s'). При появлении в зоне видимости добавляй класс 'visible' с opacity: 1 и transform: translateY(0). Начальное состояние: opacity: 0, translateY(20px). Код на TypeScript.»

Пример кода (FadeInOnScroll.tsx):

import React, { useRef, useEffect, useState } from 'react';

interface FadeInOnScrollProps {
  children: React.ReactNode;
  threshold?: number;
  duration?: string;
}

export const FadeInOnScroll: React.FC<FadeInOnScrollProps> = ({
  children,
  threshold = 0.1,
  duration = '0.5s',
}) => {
  const ref = useRef<HTMLDivElement>(null);
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.unobserve(entry.target);
        }
      },
      { threshold }
    );

    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, [threshold]);

  return (
    <div
      ref={ref}
      style={{
        opacity: isVisible ? 1 : 0,
        transform: isVisible ? 'translateY(0)' : 'translateY(20px)',
        transition: `opacity ${duration}, transform ${duration}`,
      }}
    >
      {children}
    </div>
  );
};

Использование: <FadeInOnScroll><h1>Привет, мир!</h1></FadeInOnScroll>

7. Таблица с сортировкой и пагинацией

Когда использовать: Отображение больших списков данных.

Промт: «Создай React компонент DataTable на TypeScript. Принимает пропсы: columns (массив объектов с key, label), data (массив объектов). Реализуй сортировку по клику на заголовок столбца (переключение asc/desc) и пагинацию с выбором количества строк на странице (10, 20, 50). Используйте useState для управления состоянием. Стили — базовые inline.»

Пример кода (сокращённо):

import React, { useState, useMemo } from 'react';

interface Column {
  key: string;
  label: string;
}

interface DataTableProps {
  columns: Column[];
  data: Record<string, any>[];
}

export const DataTable: React.FC<DataTableProps> = ({ columns, data }) => {
  const [sortKey, setSortKey] = useState<string | null>(null);
  const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
  const [page, setPage] = useState(0);
  const [pageSize, setPageSize] = useState(10);

  const sortedData = useMemo(() => {
    if (!sortKey) return data;
    return [...data].sort((a, b) => {
      if (a[sortKey] < b[sortKey]) return sortDir === 'asc' ? -1 : 1;
      if (a[sortKey] > b[sortKey]) return sortDir === 'asc' ? 1 : -1;
      return 0;
    });
  }, [data, sortKey, sortDir]);

  const paginatedData = sortedData.slice(page * pageSize, (page + 1) * pageSize);
  const totalPages = Math.ceil(sortedData.length / pageSize);

  const handleSort = (key: string) => {
    if (key === sortKey) {
      setSortDir((prev) => (prev === 'asc' ? 'desc' : 'asc'));
    } else {
      setSortKey(key);
      setSortDir('asc');
    }
  };

  return (
    <div>
      <table>
        <thead>
          <tr>
            {columns.map((col) => (
              <th key={col.key} onClick={() => handleSort(col.key)}>
                {col.label} {sortKey === col.key ? (sortDir === 'asc' ? '▲' : '▼') : ''}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {paginatedData.map((row, i) => (
            <tr key={i}>
              {columns.map((col) => (
                <td key={col.key}>{row[col.key]}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
      <div>
        <button disabled={page === 0} onClick={() => setPage((p) => p - 1)}>Назад</button>
        <span>Страница {page + 1} из {totalPages}</span>
        <button disabled={page >= totalPages - 1} onClick={() => setPage((p) => p + 1)}>Вперёд</button>
        <select value={pageSize} onChange={(e) => { setPageSize(Number(e.target.value)); setPage(0); }}>
          <option value={10}>10</option>
          <option value={20}>20</option>
          <option value={50}>50</option>
        </select>
      </div>
    </div>
  );
};

Совет: Для больших данных (более 10 000 строк) используйте виртуализацию — добавьте в промт «используй react-window».

8. Подключение и настройка React Query (TanStack Query)

Когда использовать: Управление серверным состоянием и кеширование.

Промт: «Создай кастомный хук usePosts с использованием @tanstack/react-query. Хук должен получать список постов из API https://jsonplaceholder.typicode.com/posts. Настрой кеширование на 5 минут (staleTime: 300000). Возвращай объект { data, isLoading, isError, error }. Также создай компонент PostsList, который использует этот хук и отображает список с индикатором загрузки и обработкой ошибок. Код на TypeScript.»

Пример кода:

import { useQuery } from '@tanstack/react-query';

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

const fetchPosts = async (): Promise<Post[]> => {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts');
  if (!res.ok) throw new Error('Network error');
  return res.json();
};

export const usePosts = () => {
  return useQuery<Post[], Error>({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    staleTime: 300000, // 5 minutes
  });
};

// PostsList.tsx
import { usePosts } from './usePosts';

export const PostsList = () => {
  const { data, isLoading, isError, error } = usePosts();

  if (isLoading) return <p>Загрузка...</p>;
  if (isError) return <p>Ошибка: {error.message}</p>;

  return (
    <ul>
      {data?.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
};

Важно: Не забудьте обернуть приложение в QueryClientProvider.

9. Динамическая загрузка компонентов (Lazy Loading)

Когда использовать: Оптимизация размера бандла.

Промт: «Напиши пример динамического импорта компонента в Next.js с помощью next/dynamic. Создай страницу, которая загружает тяжёлый компонент Chart только при нажатии кнопки. Пока компонент загружается, покажи спиннер. Используй TypeScript. Компонент Chart должен быть в отдельном файле и экспортироваться по умолчанию.»

Пример кода:

// app/dashboard/page.tsx
import dynamic from 'next/dynamic';
import { useState } from 'react';

const DynamicChart = dynamic(() => import('./Chart'), {
  loading: () => <p>Загрузка графика...</p>,
  ssr: false, // отключаем SSR для клиентского компонента
});

export default function DashboardPage() {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <h1>Дашборд</h1>
      <button onClick={() => setShowChart(true)}>Показать график</button>
      {showChart && <DynamicChart />}
    </div>
  );
}

// app/dashboard/Chart.tsx
const Chart = () => {
  // Тяжёлая логика
  return <div>График загружен</div>;
};
export default Chart;

Совет: Для компонентов, которые не нужны на всех страницах, всегда используйте lazy loading — это уменьшает начальный бандл.

10. Генерация тестов для компонента

Когда использовать: Обеспечение качества кода.

Промт: «Напиши тесты на Jest и React Testing Library для компонента Button (из первого промта). Проверь: рендеринг с разными вариантами (primary, secondary), вызов onClick при клике, наличие класса для размера lg. Используй describe и it. Имитируй события через fireEvent.»

Пример кода (Button.test.tsx):

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

describe('Button', () => {
  it('renders primary variant with correct text', () => {
    render(<Button variant="primary">Click me</Button>);
    const button = screen.getByText('Click me');
    expect(button).toBeInTheDocument();
    expect(button).toHaveClass('primary');
  });

  it('calls onClick when clicked', () => {
    const onClick = jest.fn();
    render(<Button onClick={onClick}>Click</Button>);
    fireEvent.click(screen.getByText('Click'));
    expect(onClick).toHaveBeenCalledTimes(1);
  });

  it('applies large size class', () => {
    render(<Button size="lg">Large</Button>);
    expect(screen.getByText('Large')).toHaveClass('lg');
  });
});

Заключение

Эти 10 промтов покрывают 80% задач, с которыми я сталкиваюсь в коммерческой разработке на React и Next.js. Адаптируйте их под свои проекты: добавляйте специфичные пропсы, меняйте API, комбинируйте несколько промтов в один. Главное — всегда уточняйте контекст: фреймворк, библиотеки, стили. В 2026 году AI-инструменты стали незаменимыми помощниками, но качество результата напрямую зависит от того, как вы формулируете запрос. Экспериментируйте, и ваша продуктивность вырастет в разы.

← Все статьи

Комментарии

Читайте также

Мешают ли нейросети мышлению: разбираемся на примере нового исследования

19 июля 2026

Жилищное право 2026: как AI-обучение помогает разобраться в ЖК РФ и решить квартирный вопрос без юриста

19 июля 2026

Как ИИ-агент интегрируется с WordPress: автоматизация управления контентом без кода

19 июля 2026

Как построить надежную программу ПОД/ФТ в 2026 году: пошаговое руководство с курсом «Специалист по ПОД/ФТ»

19 июля 2026

ESP32 VGA (DAC) + ASI Biont: как вывести данные датчиков и AI-графику на аналоговый монитор без видеоконтроллера

19 июля 2026

Как освоить ROS 2, SLAM и компьютерное зрение: обзор курса «Автономные системы и робототехника» на Asibiont

19 июля 2026

Elasticsearch + ASI Biont: AI-агент для автоматизации поиска аномалий и аналитики логов без кода

19 июля 2026

Red Team & Application Security: Как AI-тьютор помогает освоить пентест с нуля и защиту приложений

19 июля 2026

Почему психология и когнитивная наука — это ключевой навык для эпохи ИИ: карьерное руководство

19 июля 2026