К основному содержанию
скилл CX Codex

nextjs-client-cookie-pattern — скилл для Claude Code: куки из клиента

Скилл показывает, как из клиентского компонента Next.js выставить серверную куку через server action. Пригодится для тем, авторизации и языковых настроек.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add wsimmonds/claude-nextjs-skills --skill nextjs-client-cookie-pattern -a claude-code
Открыть на GitHub

Что делает nextjs-client-cookie-pattern

В Next.js клиентский компонент не может писать куки — браузер этого не разрешает. Скилл описывает обходной путь из двух файлов: в app/CookieButton.tsx стоит директива 'use client' и обработчик onClick, а в app/actions.ts — 'use server' и вызов cookies() из next/headers. Клиент импортирует функцию из второго файла и вызывает её по клику.

Внутри разобраны готовые примеры: переключатель темы, баннер согласия на куки, выбор языка, форма с FormData и вход с редиректом через redirect из next/navigation. Отдельный блок про опции cookieStore.set: httpOnly, secure, sameSite, maxAge, path. Есть пометка про @typescript-eslint/no-explicit-any — типы any в этом коде не пройдут линтер, поэтому параметры подписывают как string.

Скилл читает и правит файлы, ищет по проекту и запускает команды в оболочке. Если у вас в проекте уже есть свой слой работы с сессиями, паттерн придётся подгонять под него, иначе получится две точки записи кук.

Какие задачи решает

  • выставить куку по клику кнопки в клиентском компоненте
  • сохранить выбор темы между заходами
  • записать согласие на куки из баннера
  • передать выбранный язык в серверную куку
  • сохранить настройки формы через FormData

Примеры запросов агенту

  • Помоги вынести установку куки в server action для кнопки переключения темы
  • Сделай баннер согласия на куки по паттерну клиент плюс server action
  • Проверь, что в actions.ts нет типов any и куки ставятся с httpOnly

Как установить nextjs-client-cookie-pattern

1Через Skills CLI (Vercel)

npx skills add wsimmonds/claude-nextjs-skills --skill nextjs-client-cookie-pattern -a claude-code

2Вручную: положить файлы

~/.claude/skills/nextjs-client-cookie-pattern/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/nextjs-client-cookie-pattern/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора6

Похожие

nextjs-app-router giuseppe-trisciuoglio Даёт паттерны и примеры для Next.js 16+ с App Router: от Server Components до route.ts и кэширования. CX Codex +1 3 900установок clerk-nextjs-patterns clerk Скилл обучает ИИ-агента паттернам аутентификации Clerk в Next.js: от серверных функций до кэширования. Пригодится при настройке middleware, Server Actions и защите роутов. CX Codex +1 53 400установок nextjs-code-review giuseppe-trisciuoglio Проверяет код Next.js перед слиянием изменений: компоненты, серверные действия, кэш и маршруты. Пригодится после новой функции или миграции. CX Codex +1 3 800установок nextjs-cache-architecture mohamed-hossam1 Выстраивает архитектуру кеширования для Next.js 16+ App Router. Помогает внедрить директиву "use cache", собрать типизированный реестр тегов и настроить границы Suspense. CX Codex +1 5 000установок nextjs-typescript-tailwindcss-supabase Mindrally Скилл задаёт агенту правила для Next.js 14, TypeScript, TailwindCSS и Supabase: серверные компоненты, обработка ошибок, читаемый код. CX Codex +1 1 300установок nextjs-developer VoltAgent Community Субагент проектирует и пишет full-stack приложения на Next.js 14+ с App Router. Он помогает настроить Server Actions, оптимизировать Core Web Vitals и подготовить SEO. 898установок

Рядом в теме «Фронтенд и UI»

Всё по теме «Фронтенд и UI» →

Вопросы о nextjs-client-cookie-pattern

Как установить nextjs-client-cookie-pattern в Claude?

Выполните npx skills add wsimmonds/claude-nextjs-skills --skill nextjs-client-cookie-pattern -a claude-code. Файл скилла окажется в ~/.claude/skills/nextjs-client-cookie-pattern/SKILL.md.

Что делает nextjs-client-cookie-pattern?

Описывает схему из двух файлов: клиентский компонент с обработчиком события вызывает server action, а тот через cookies() из next/headers записывает куку.

Почему нельзя поставить куку прямо в клиентском компоненте?

Клиентский код выполняется в браузере, а браузеры ограничивают работу с куками. Запись возможна только на сервере, поэтому и нужен server action.

Исходный текст

SKILL.md · MIT
---
name: nextjs-client-cookie-pattern
description: Pattern for client components calling server actions to set cookies in Next.js. Covers the two-file pattern of a client component with user interaction (onClick, form submission) that calls a server action to modify cookies. Use when building features like authentication, preferences, or session management where client-side triggers need to set/modify server-side cookies.
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
---

# Next.js: Client Component + Server Action Cookie Pattern

## Pattern Overview

This pattern handles a common Next.js requirement: **client-side interaction (button click) that needs to set server-side cookies**.

**Why Two Files?**
- Client components (`'use client'`) can have onClick handlers
- Only server code can set cookies (security requirement)
- Solution: Client component calls a server action that sets cookies

## The Pattern

**Scenario:** A button that sets a cookie when clicked

**File 1: Client Component** (`app/CookieButton.tsx`)
- Has `'use client'` directive
- Has onClick handler
- Imports and calls server action

**File 2: Server Action** (`app/actions.ts`)
- Has `'use server'` directive
- Uses `cookies()` from `next/headers`
- Sets the cookie

## Complete Implementation

### File 1: Client Component

```typescript
// app/CookieButton.tsx
'use client';

import { setPreference } from './actions';

export default function CookieButton() {
  const handleClick = async () => {
    await setPreference('dark-mode', 'true');
  };

  return (
    <button onClick={handleClick}>
      Enable Dark Mode
    </button>
  );
}
```

### File 2: Server Action

```typescript
// app/actions.ts
'use server';

import { cookies } from 'next/headers';

export async function setPreference(key: string, value: string) {
  const cookieStore = await cookies();

  cookieStore.set(key, value, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 365, // 1 year
  });
}
```

## File Structure

```
app/
├── CookieButton.tsx    ← Client component
├── actions.ts          ← Server actions
└── page.tsx            ← Uses CookieButton
```

## TypeScript: NEVER Use `any` Type

This codebase has `@typescript-eslint/no-explicit-any` enabled.

```typescript
// ❌ WRONG
async function setCookie(key: any, value: any) { ... }

// ✅ CORRECT
async function setCookie(key: string, value: string) { ... }
```

## Real-World Examples

### Example 1: Theme Toggle

```typescript
// app/ThemeToggle.tsx
'use client';

import { useState } from 'react';
import { setTheme } from './actions';

export default function ThemeToggle() {
  const [theme, setLocalTheme] = useState('light');

  const toggle = async () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setLocalTheme(newTheme);
    await setTheme(newTheme);
  };

  return (
    <button onClick={toggle} className={theme}>
      {theme === 'light' ? '🌙' : '☀️'} Toggle Theme
    </button>
  );
}

// app/actions.ts
'use server';

import { cookies } from 'next/headers';

export async function setTheme(theme: 'light' | 'dark') {
  const cookieStore = await cookies();
  cookieStore.set('theme', theme, {
    httpOnly: false, // Allow client to read it
    maxAge: 60 * 60 * 24 * 365,
  });
}
```

### Example 2: Accept Cookies Banner

```typescript
// app/components/CookieBanner.tsx
'use client';

import { useState } from 'react';
import { acceptCookies } from '../actions';

export default function CookieBanner() {
  const [visible, setVisible] = useState(true);

  const handleAccept = async () => {
    await acceptCookies();
    setVisible(false);
  };

  if (!visible) return null;

  return (
    <div className="cookie-banner">
      <p>We use cookies to improve your experience.</p>
      <button onClick={handleAccept}>Accept</button>
    </div>
  );
}

// app/actions.ts
'use server';

import { cookies } from 'next/headers';

export async function acceptCookies() {
  const cookieStore = await cookies();
  cookieStore.set('cookies-accepted', 'true', {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    maxAge: 60 * 60 * 24 * 365,
  });
}
```

### Example 3: Language Preference

```typescript
// app/LanguageSelector.tsx
'use client';

import { setLanguage } from './actions';

export default function LanguageSelector() {
  const languages = ['en', 'es', 'fr', 'de'];

  return (
    <select onChange={(e) => setLanguage(e.target.value)}>
      {languages.map((lang) => (
        <option key={lang} value={lang}>
          {lang.toUpperCase()}
        </option>
      ))}
    </select>
  );
}

// app/actions.ts
'use server';

import { cookies } from 'next/headers';

export async function setLanguage(lang: string) {
  const cookieStore = await cookies();
  cookieStore.set('language', lang, {
    httpOnly: false,
    maxAge: 60 * 60 * 24 * 365,
  });
}
```

## Cookie Options

```typescript
cookieStore.set('name', 'value', {
  httpOnly: true,    // Prevents JavaScript access (security)
  secure: true,      // Only send over HTTPS
  sameSite: 'lax',   // CSRF protection
  maxAge: 3600,      // Expires in 1 hour (seconds)
  path: '/',         // Available on all routes
});
```

## Common Variations

### With Form Submission

```typescript
// app/PreferencesForm.tsx
'use client';

import { savePreferences } from './actions';

export default function PreferencesForm() {
  return (
    <form action={savePreferences}>
      <label>
        <input type="checkbox" name="notifications" />
        Enable Notifications
      </label>
      <button type="submit">Save</button>
    </form>
  );
}

// app/actions.ts
'use server';

import { cookies } from 'next/headers';

export async function savePreferences(formData: FormData) {
  const cookieStore = await cookies();
  const notifications = formData.get('notifications') === 'on';

  cookieStore.set('notifications', String(notifications), {
    httpOnly: true,
    maxAge: 60 * 60 * 24 * 365,
  });
}
```

### With Redirect After Setting Cookie

```typescript
// app/actions.ts
'use server';

import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';

export async function login(email: string, password: string) {
  // Authenticate user
  const session = await authenticate(email, password);

  // Set session cookie
  const cookieStore = await cookies();
  cookieStore.set('session', session.token, {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 7, // 1 week
  });

  // Redirect to dashboard
  redirect('/dashboard');
}
```

## Why This Pattern?

**Can't client components set cookies directly?**
No. Client components run in the browser, and modern browsers restrict cookie manipulation for security. Server actions run on the server where cookie-setting is allowed.

**Why not use a Route Handler (API route)?**
You can! But server actions are simpler and more integrated with the Next.js App Router pattern.

```typescript
// Alternative: Route Handler approach
// app/api/set-cookie/route.ts
export async function POST(request: Request) {
  const { name, value } = await request.json();

  return new Response(null, {
    status: 200,
    headers: {
      'Set-Cookie': `${name}=${value}; HttpOnly; Path=/; Max-Age=31536000`,
    },
  });
}

// Client component
async function setCookie() {
  await fetch('/api/set-cookie', {
    method: 'POST',
    body: JSON.stringify({ name: 'theme', value: 'dark' }),
  });
}
```

Server actions are preferred because they're:
- More type-safe
- Less boilerplate
- Better integrated with forms
- Easier to test

## Reading Cookies

**In Server Components:**
```typescript
// app/page.tsx
import { cookies } from 'next/headers';

export default async function Page() {
  const cookieStore = await cookies();
  const theme = cookieStore.get('theme')?.value || 'light';

  return <div className={theme}>Content</div>;
}
```

**In Client Components:**
```typescript
// Can't use next/headers in client components!
// Use document.cookie or a state management library
'use client';

import { useEffect, useState } from 'react';

export default function ThemeDisplay() {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    // Read from document.cookie
    const cookieTheme = document.cookie
      .split('; ')
      .find(row => row.startsWith('theme='))
      ?.split('=')[1];

    if (cookieTheme) setTheme(cookieTheme);
  }, []);

  return <div>Current theme: {theme}</div>;
}
```

## Quick Checklist

When you need to set cookies from a button click:

- [ ] Create client component with `'use client'`
- [ ] Add onClick handler or form submission
- [ ] Create server action file (e.g., `app/actions.ts`)
- [ ] Add `'use server'` directive
- [ ] Import `cookies` from `next/headers`
- [ ] Await `cookies()` (Next.js 15+)
- [ ] Call `cookieStore.set(name, value, options)`
- [ ] Import server action in client component
- [ ] Call server action from handler

## Summary

**Client-Server Cookie Pattern:**
- ✅ Client component handles user interaction
- ✅ Server action sets the cookie
- ✅ Two files: component + actions
- ✅ Type-safe with proper TypeScript
- ✅ Secure (httpOnly, secure, sameSite options)

This pattern is the recommended way to handle client-triggered cookie operations in Next.js App Router.

© wsimmonds · MIT · wsimmonds/claude-nextjs-skills

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой