ISR: Incremental Static Regeneration

ISR (Incremental Static Regeneration) — стратегия рендеринга в Next.js, при которой статические страницы пересобираются в фоне по запросу или по таймеру, не требуя полного rebuild проекта.

Зачем нужно

SSG даёт максимальную скорость, но данные устаревают сразу после сборки. SSR обновляет данные на каждый запрос, но требует сервера и медленнее отвечает. ISR — компромисс: страница отдаётся как статика (быстро), а фоновое обновление запускается после истечения revalidate-таймаута. Пользователь всегда получает ответ мгновенно; устаревшая страница обновляется в фоне и при следующем запросе уже актуальна.

Где используется

  • E-commerce: страницы товаров — данные актуальны, но не нужно SSR на каждый запрос
  • Новостные сайты: статьи обновляются редко, но их много
  • Документация с версионированием
  • Любой контент, который меняется, но не в реальном времени

Как работает ISR

1. При сборке (build): страница генерируется как статический HTML
2. При первом запросе: отдаётся кэшированный HTML (мгновенно)
3. После истечения revalidate (например, 60 секунд):
   - Следующий запрос всё равно получает кэш
   - В фоне запускается регенерация страницы
4. После завершения регенерации: новые запросы получают обновлённый HTML

ISR в Pages Router (Next.js)

// pages/products/[id].jsx

export async function getStaticPaths() {
  // Генерируем только популярные страницы при сборке
  const topProducts = await fetchTopProducts;
  return {
    paths: topProducts.map(p => ({ params: { id: p.id } })),
    // fallback: 'blocking' — первый запрос к неизвестной странице
    // ждёт SSR-генерацию, потом кэширует
    fallback: 'blocking',
  };
}

export async function getStaticProps({ params }) {
  const product = await fetchProduct(params.id);

  if (!product) {
    return { notFound: true };
  }

  return {
    props: { product },
    // Страница будет регенерироваться не чаще чем раз в 60 секунд
    revalidate: 60,
  };
}

export default function ProductPage({ product }) {
  return (
    <article>
      <h1>{product.name}</h1>
      <p>{product.price} ₽</p>
    </article>
  );
}

On-Demand ISR (по требованию)

// pages/api/revalidate.js
// Ручной тригер ревалидации (например, из CMS webhook)
export default async function handler(req, res) {
  // Защита от неавторизованных вызовов
  if (req.query.secret !== process.env.REVALIDATE_SECRET) {
    return res.status(401).json({ message: 'Invalid token' });
  }

  try {
    const path = req.query.path; // например, '/products/42'
    await res.revalidate(path);
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

// Вызов из CMS при изменении контента:
// POST /api/revalidate?secret=TOKEN&path=/products/42

Частые ошибки

  • Слишком короткий revalidate — значение 1 секунда превращает ISR в дорогой SSR; устанавливайте реалистичный интервал исходя из частоты изменений.
  • Не настраивают fallback — при fallback: false запрос к несуществующей странице вернёт 404; blocking или true позволяют генерировать новые страницы на лету.
  • Ожидают мгновенного обновления — стратегия stale-while-revalidate означает, что первый запрос после истечения таймаута всё равно получит устаревшую страницу.

Связанные темы

Ресурсы