Qadam Roadmap
проектdocs/Agents/rules/architecture-server-components.md

architecture-server-components.md

Обновлён 1 апр. 2026 г., 12:41 · 0 комментариев


title: Default to Server Components, Use Client Only When Needed impact: CRITICAL impactDescription: Reduces JavaScript bundle size by 40-60% and improves initial load tags: architecture, nextjs, rsc, server-components, ssr

Паспорт документа

  • Статус документа: living standard
  • Актуально на: 28 марта 2026 года
  • Владелец: backend/platform-команда
  • Пересмотр: при изменении инженерной практики, CI/CD, архитектурных правил или локального workflow
  • Область применения: внутренние rule/reference-card документы для инженерной команды
  • Связанные документы:

Default to Server Components, Use Client Only When Needed

Impact: CRITICAL (Reduces JS bundle size by 40-60%)

Every Next.js component is a Server Component by default. Only add "use client" when the component genuinely requires browser APIs or interactivity.

Requires "use client":

  • Event handlers (onClick, onChange, onSubmit)
  • React hooks (useState, useEffect, useRef)
  • Browser APIs (window, document, localStorage)
  • Third-party client libraries (map widgets, animation libraries)

Does NOT require "use client":

  • Data fetching and display
  • Static content rendering
  • Layout and navigation structure
  • Conditional rendering based on props/data

Incorrect (unnecessary client component):

"use client"; // Why? This doesn't need any client features!

import { api } from '@/lib/api';

export default function ItemDetailPage({ params }) {
  const [item, setItem] = useState(null);

  useEffect(() => {
    api.catalog.getItemBySlug(params.slug).then(setItem);
  }, []);

  if (!item) return <Skeleton />;
  return <div>{item.name}</div>;
}

Correct (Server Component with client islands):

// app/item/[slug]/page.tsx — Server Component (no directive)
import { api } from '@/lib/api';
import { notFound } from 'next/navigation';
import { ItemActions } from './ItemActions'; // Client island

export default async function ItemDetailPage({ params }: { params: { slug: string } }) {
  const item = await api.catalog.getItemBySlug(params.slug);
  if (!item) notFound();

  return (
    <div>
      <h1>{item.name}</h1>
      <p>{item.description}</p>
      {/* Only this part is a Client Component */}
      <ItemActions itemId={item.id} />
    </div>
  );
}

// app/item/[slug]/ItemActions.tsx — Client Component (needs interactivity)
"use client";

import { useTranslations } from 'next-intl';

export function ItemActions({ itemId }: { itemId: string }) {
  const t = useTranslations('item');
  const [showModal, setShowModal] = useState(false);
  return <button onClick={() => setShowModal(true)}>{t('submitLead')}</button>;
}

Pattern: Client Islands Keep the page as a Server Component. Extract only the interactive parts into small Client Components ("islands") and pass serializable data as props.