AI студия Владимира Ломтева
УСЛУГИПРОЕКТЫСТАТЬИБАЗА ЗНАНИЙМаркетплейсПолезные сервисы

Оставьте заявку,
чтобы обсудить проект

Напишите ваш вопрос, не забудьте указать телефон. Мы перезвоним и все расскажем.

Контакты

Москва

Работаем по всей России
и миру (онлайн)

+7 (999) 760-24-41

Ежедневно с 9:00 до 21:00

lamooof@gmail.com

По вопросам сотрудничества

TelegramWhatsApp

Есть предложение?

Напишите нам в мессенджеры

© 2025 AI студия Владимира Ломтева

Политика конфиденциальностиСогласие на обработку ПДн|ИНН 623412173261

    Nextjs Cache Architecture

    Use this skill whenever the user wants to design or implement caching in a Next.js 16+ App Router project — setting up the "use cache" directive, building a cache tag registry, wiring mutations to invalidation utilities, structuring Suspense boundaries for partial prerendering, handling personalized content near cache boundaries, choosing cacheLife profiles, calling cacheTag / updateTag / revalidateTag correctly, migrating from unstable cache, or debugging stale or incorrectly fresh data. Trigger even when the user only describes their domain (e.g. "I have a posts table") and asks how to cache it properly.

    Скиллы для маркетинга#GitHub#mohamed-hossam1/nextjs-skills#skills.sh
    Скачивания
    0
    В избранном
    0
    Комментарии
    0
    Просмотры

    Установить скилл

    Добавьте инструмент одной командой или скачайте проверенный архив версии.

    npx skills add mohamed-hossam1/nextjs-skills --skill nextjs-cache-architecture
    Скачать ZIP
    Версия
    1.0.0+80d20aaf2aab
    Автор
    Владимир Ломтев
    Репозиторий
    mohamed-hossam1/nextjs-skills
    GitHub: mohamed-hossam1/nextjs-skills
    Nextjs Cache Architecture — Скилл для ИИ-агентов | AI Рассвет
    2

    Установить скилл

    Добавьте инструмент одной командой или скачайте проверенный архив версии.

    npx skills add mohamed-hossam1/nextjs-skills --skill nextjs-cache-architecture
    Скачать ZIP
    Версия
    1.0.0+80d20aaf2aab
    Автор
    Владимир Ломтев
    Репозиторий
    mohamed-hossam1/nextjs-skills
    GitHub: mohamed-hossam1/nextjs-skills

    Как установить

    1. 1Скопируйте команду из блока установки.
    2. 2Запустите её в терминале из каталога проекта.

    Документация

    Next.js Cache Architecture

    Architect caching in a Next.js 16+ App Router project from day one — not just dropping "use cache" where it happens to fit, but structuring the tag registry, revalidation utilities, Suspense boundaries, and mutation wiring so the cache stays correct as the codebase grows.

    How to use this skill

    Apply every rule and template below to the user's actual project. Replace placeholders like [Entity] and [collection] with names from their codebase before writing any code.

    $ARGUMENTS
    

    Where to look next

    Most implementations only need this file. Load a reference when the task calls for it.

    If the user is... Read
    Asking how cache keys are derived, what cacheLife profiles mean, or hitting a "use cache" limitation references/core-concepts.md
    Caching anything that depends on a logged-in user references/personalized-content.md
    Reporting stale data, or doing a final review pass references/debugging-and-checklist.md
    Migrating an existing codebase off unstable_cache references/migration-from-unstable-cache.md

    Drop-in templates in assets/ (rename placeholders to match the user's codebase):

    • assets/tags.ts → lib/cache/tags.ts
    • assets/revalidate.ts → lib/cache/revalidate.ts
    • assets/SuspenseOnSearchParams.tsx → components/SuspenseOnSearchParams.tsx

    The architecture in one breath

    A correct cache implementation has three load-bearing pieces. Build all three on day one — adding them later is much harder than getting them right up front.

    1. Tag registry (lib/cache/tags.ts) — every tag string lives here. No raw strings anywhere else.
    2. Revalidation utilities (lib/cache/revalidate.ts) — every updateTag() lives here. Mutations import from this file.
    3. Cache placement on data, not on pages — "use cache" goes on data-fetching functions or cached child components. Page components orchestrate Suspense boundaries; the children fetch.

    Once those three are in place, the rest is just applying them consistently.

    Step 1 — Enable Cache Components

    // next.config.ts
    import type { NextConfig } from "next";
    
    const nextConfig: NextConfig = {
      cacheComponents: true,
    };
    
    export default nextConfig;
    

    Step 2 — Build the cache tag registry

    File: lib/cache/tags.ts (template: assets/tags.ts)

    Use the assets/tags.ts template. The as const satisfies TagRegistry shape gives literal types and rejects malformed entries at compile time.

    // lib/cache/tags.ts (skeleton — full template in assets/tags.ts)
    
    export const CACHE_TAGS = {
      // Collection tags — one per logical data group, always present.
      [collection]: "[collection]",
    
      // Entity tag factories — only when a mutation targets a single entry.
      [entity]: (id: string | number) => `[entity]:${id}`,
    } as const;
    

    Step 3 — Build revalidation utilities

    File: lib/cache/revalidate.ts (template: assets/revalidate.ts)

    All updateTag() calls live here. Mutations import these functions — they never call updateTag() directly.

    // lib/cache/revalidate.ts
    "use server";
    
    import { updateTag } from "next/cache";
    import { CACHE_TAGS } from "./tags";
    
    function updateTags(tags: string[]) {
      for (const tag of tags) updateTag(tag);
    }
    
    // Bulk — any entry in the collection changed.
    export async function revalidate[Collection]Cache() {
      updateTags([CACHE_TAGS.[collection]]);
    }
    
    // Surgical — one specific entry changed.
    // Only write this if `CACHE_TAGS.[entity]` factory exists in the registry.
    export async function revalidate[Entity]Cache(id: string | number) {
      updateTags([
        CACHE_TAGS.[collection], // always invalidate the parent collection too
        CACHE_TAGS.[entity](id),
      ]);
    }
    

    Step 4 — Implement data fetching

    Place "use cache" in data-fetching functions. Never fetch inside page components — page components orchestrate, they do not fetch.

    // lib/data/[domain].ts
    import { cacheLife, cacheTag } from "next/cache";
    import { CACHE_TAGS } from "@/lib/cache/tags";
    
    const BASE_URL = process.env.API_BASE_URL!;
    
    // Good: collection fetch.
    export async function get[Collection]() {
      "use cache";
      cacheLife("hours");
      cacheTag(CACHE_TAGS.[collection]);
    
      const res = await fetch(`${BASE_URL}/[endpoint]`);
      return res.json();
    }
    
    // Good: entity fetch.
    export async function get[Entity](id: string) {
      "use cache";
      cacheLife("hours");
      cacheTag(CACHE_TAGS.[collection]);
      // Add CACHE_TAGS.[entity](id) only if a mutation calls updateTag on this entry.
    
      const res = await fetch(`${BASE_URL}/[endpoint]/${id}`);
      return res.json();
    }
    
    // Bad: fetching in a page component bypasses caching and invalidation.
    export default async function Page() {
      const res = await fetch("/api/items");
      const data = await res.json();
      return <View data={data} />;
    }
    

    Step 5 — Structure rendering boundaries

    Every page follows this shape:

    Page component (sync, orchestration only — no data fetching)
      ├── Static shell (layout, nav — no data)
      ├── <Suspense> → cached shared content
      └── <Suspense> → dynamic personalized content
    

    Standard page

    // app/[route]/page.tsx
    import { Suspense } from "react";
    import { cacheLife, cacheTag } from "next/cache";
    import { CACHE_TAGS } from "@/lib/cache/tags";
    import { get[Collection] } from "@/lib/data/[domain]";
    
    export default function AnyPage() {
      return (
        <>
          <StaticShell />
    
          <Suspense fallback={<SharedSkeleton />}>
            <SharedContent />
          </Suspense>
    
          <Suspense fallback={<PersonalizedSkeleton />}>
            <PersonalizedSection />
          </Suspense>
        </>
      );
    }
    
    async function SharedContent() {
      "use cache";
      cacheLife("hours");
      cacheTag(CACHE_TAGS.[collection]);
    
      const data = await get[Collection]();
      return <[Collection]List data={data} />;
    }
    

    Dynamic route page

    // app/[domain]/[id]/page.tsx
    import { Suspense } from "react";
    import { cacheLife, cacheTag } from "next/cache";
    import { CACHE_TAGS } from "@/lib/cache/tags";
    import { get[Entity] } from "@/lib/data/[domain]";
    
    export default function EntityPage({
      params,
    }: {
      params: Promise<{ id: string }>;
    }) {
      return (
        <Suspense fallback={<EntitySkeleton />}>
          <EntityDetail params={params} />
        </Suspense>
      );
    }
    
    async function EntityDetail({
      params,
    }: {
      params: Promise<{ id: string }>;
    }) {
      const { id } = await params;
      return <CachedEntityView id={id} />;
    }
    
    async function CachedEntityView({ id }: { id: string }) {
      "use cache";
      cacheLife("hours");
      cacheTag(CACHE_TAGS.[collection]);
      // Add CACHE_TAGS.[entity](id) only if a mutation needs surgical invalidation.
    
      const item = await get[Entity](id);
      return <[Entity]View item={item} />;
    }
    

    Filtered / search params page

    // app/[route]/page.tsx
    import { cacheLife, cacheTag } from "next/cache";
    import { CACHE_TAGS } from "@/lib/cache/tags";
    import { get[Collection]ByFilter } from "@/lib/data/[domain]";
    import SuspenseOnSearchParams from "@/components/SuspenseOnSearchParams";
    
    export default function FilteredPage({
      searchParams,
    }: {
      searchParams: Promise<Record<string, string>>;
    }) {
      return (
        <SuspenseOnSearchParams fallback={<FilteredListSkeleton />}>
          <FilteredList searchParams={searchParams} />
        </SuspenseOnSearchParams>
      );
    }
    
    async function FilteredList({
      searchParams,
    }: {
      searchParams: Promise<Record<string, string>>;
    }) {
      "use cache";
      cacheLife("minutes");
      cacheTag(CACHE_TAGS.[collection]);
      // searchParams is an argument → auto-keyed per unique param combination.
    
      const { q = "", page = "1" } = await searchParams;
      return await get[Collection]ByFilter(q, page);
    }
    

    A standard <Suspense> does not re-trigger its fallback on client-side navigation when only searchParams changes. Use SuspenseOnSearchParams (template: assets/SuspenseOnSearchParams.tsx) on every page with search or filter params.

    Step 6 — Handle personalized content

    Read cookies() / headers() / auth() outside the cache boundary and pass the value as a prop. The argument becomes part of the auto-generated cache key, so each user gets their own entry. Calling any of those APIs inside a "use cache" function throws or produces wrong behavior.

    See references/personalized-content.md for the full read-outside / cache-inside pattern and the rare "use cache: private" exception.

    Step 7 — Wire mutations to invalidation

    Mutations call revalidation utilities and never reach for updateTag() themselves. This keeps the cache layer mechanical and auditable from one file, and lets you add observability (logging, tracing) in one place.

    // app/actions/[domain].ts
    "use server";
    
    import {
      revalidate[Collection]Cache,
      revalidate[Entity]Cache,
    } from "@/lib/cache/revalidate";
    
    export async function create[Entity](payload: unknown) {
      await db.[entity].create(payload);
      await revalidate[Collection]Cache();
    }
    
    export async function update[Entity](id: string | number, payload: unknown) {
      await db.[entity].update(id, payload);
      await revalidate[Entity]Cache(id); // requires the surgical utility to be exported
    }
    

    updateTag vs revalidateTag

    Two APIs for two different needs:

    API Effect Call from
    updateTag(tag) Immediate — the same request sees fresh data Server actions, via revalidate.ts
    revalidateTag(tag, "max") Background stale-while-revalidate — next request sees fresh data Route handlers, webhooks

    revalidateTag always takes a second argument ("max" for stale-while-revalidate, { expire: 0 } for immediate hard expiry). The single-argument form is deprecated and silently does nothing in some configurations.

    Common mistakes

    When the cache misbehaves, walk these in order. The first six catch nearly everything; only run next build after the rest pass. The full debug walk and a sign-off checklist are in references/debugging-and-checklist.md.

    Symptom or smell Fix
    Function runs uncached on every request "use cache" is after an await — move it to be the first statement.
    Cached function throws or returns wrong data per user Move cookies() / headers() / auth() outside; pass values as arguments.
    updateTag does nothing Tag string typo, or no cacheTag ever registered the matching tag.
    Mutation completes but the list still reads stale Revalidation utility called before the write, or not called at all.
    Whole page re-renders even though only one section changed A dynamic child sits inside a cached parent — split with <Suspense>.
    Filter UI doesn't show a loading state on navigation Plain <Suspense> — switch to SuspenseOnSearchParams.
    Page marked dynamic when you expected static Run next build; trace the leaked dynamic API in the route's source tree.
    Page component fetches data directly Move the fetch into a cached child; pages should orchestrate, not fetch.

    For the full debug walk and a sign-off checklist, see references/debugging-and-checklist.md. To verify the static parts of a finished implementation against the user's project, run scripts/audit.mjs <project-root> — usage and what it checks are documented in README.md.

    Требования и возможности

    Источник пакета
    https://github.com/mohamed-hossam1/nextjs-skills/tree/80d20aaf2aabf8d26e18449b007980232ea9a178/skills/nextjs-cache-architecture

    Файлы версии

    ПутьРазмерSHA256
    SKILL.md13264f324b456a36c8009...
    assets/SuspenseOnSearchParams.tsx8233a5d0e210726c197...
    assets/revalidate.ts1409675ff34029b2d6b6...
    assets/tags.ts13928b62f8bb49fa9635...
    evals/evals.json5346e7cd196333c4f3ba...

    Частые вопросы

    Как установить Nextjs Cache Architecture?
    Используйте команду npx skills add mohamed-hossam1/nextjs-skills --skill nextjs-cache-architecture или скачайте ZIP-архив.
    Можно ли скачать Nextjs Cache Architecture бесплатно?
    Да, опубликованную версию можно скачать из маркетплейса бесплатно.

    Похожие инструменты

    Смотреть все
    Nuxt UiBuild UIs with @nuxt/ui v4 — 125+ accessible Vue components with Tailwind CSS theming. Use when creating interfaces, customizing themes to match a brand, building forms, or composing layouts like dashboards, docs sites, and chat interfaces.Gemini Api DevUse this skill when building applications with Gemini API hosted models, including Gemini and Gemma 4, working with multimodal content (text, images, audio, video), implementing function calling, using structured outputs, or needing current model specifications. Covers SDK usage (google-genai for Python, @google/genai for JavaScript/TypeScript, com.google.genai:google-genai for Java, google.golang.org/genai for Go), model selection, and API capabilities.Browser ActBrowser automation CLI for AI agents. NEVER run browser-act commands directly via Bash — always invoke this skill first. Use browser-act when a user mentions it by name, includes or asks to run a browser-act CLI command (e.g., browser-act browser list), or to: fetch, view, or extract rendered content from URLs, access pages requiring JavaScript, handle verification prompts, maintain authenticated sessions, fill forms and click through workflows, type, select, upload, take screenshots, capture XHR/fetch/HAR responses, open multiple URLs in parallel, extract content that loads on scroll or clickSeo AuditWhen the user wants to audit, review, or diagnose SEO issues on their site. Also use when the user mentions "SEO audit," "technical SEO," "why am I not ranking," "SEO issues," "on-page SEO," "meta tags review," "SEO health check," "my traffic dropped," "lost rankings," "not showing up in Google," "site isn't ranking," "Google update hit me," "page speed," "core web vitals," "crawl errors," or "indexing issues." Use this even if the user just says something vague like "my SEO is bad" or "help with SEO" — start with an audit. For building pages at scale to target keywords, see programmatic-seo.
    Комментарии

    Войдите, чтобы оставить комментарий.

    Комментариев пока нет.