Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
wsimmonds avatar

Nextjs Client Cookie Pattern

  • 287 installs
  • 109 repo stars
  • Updated October 24, 2025
  • wsimmonds/claude-nextjs-skills

nextjs-client-cookie-pattern is an agent skill that implements the two-file Next.js pattern where client components call server actions to set cookies so developers building authentication, preferences, or session featur

About

nextjs-client-cookie-pattern is a wsimmonds/claude-nextjs-skills agent skill documenting the two-file App Router pattern for client-triggered server-side cookie changes. A client component handles user interaction—button clicks or form submissions—and invokes a server action that calls cookies() from next/headers to set or modify HttpOnly session, auth, or preference values. Developers reach for nextjs-client-cookie-pattern when building login flows, theme toggles, or session refresh UX that must not expose cookie mutation logic to the browser bundle. The skill explicitly covers the boundary where client components cannot directly write secure cookies and server actions become the sanctioned bridge. It fits Next.js engineers implementing authentication, preference persistence, or session management in React Server Components architectures. Allowed tools include Read, Write, Edit, Glob, Grep, and Bash for scaffolding the paired client and server action files.

  • nextjs-client-cookie-pattern
  • AI & Agent Building
  • AI-coding skill

Nextjs Client Cookie Pattern by the numbers

  • 287 all-time installs (skills.sh)
  • +5 installs in the week ending Jul 27, 2026 (Skillselion tracking)
  • Ranked #2,387 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wsimmonds/claude-nextjs-skills --skill nextjs-client-cookie-pattern

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs287
repo stars109
Last updatedOctober 24, 2025
Repositorywsimmonds/claude-nextjs-skills

How do client components set cookies in Next.js App Router?

Helps with ai & agent building tasks.

Who is it for?

Next.js developers implementing auth, session, or preference cookies triggered by client UI events in the App Router.

Skip if: Pages Router projects, purely server-rendered forms without client interactivity, or cookie logic that belongs in Route Handlers or middleware only.

When should I use this skill?

The user builds Next.js features where client onClick or form submission must set or modify server-side cookies for auth or preferences.

What you get

Paired client component and server action files with cookie mutation via next/headers cookies() API.

  • Client component with interaction handler
  • Server action setting cookies
  • Auth or preference cookie flow

By the numbers

  • Implements a two-file client component plus server action cookie pattern

Files

SKILL.mdMarkdownGitHub ↗

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

// 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

// 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.

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

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

Real-World Examples

Example 1: Theme Toggle

// 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

// 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

// 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

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

// 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

// 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.

// 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:

// 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:

// 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.

Related skills

How it compares

Use nextjs-client-cookie-pattern instead of middleware-only cookie guides when the write must be triggered by specific client UI events like button clicks or form submits.

FAQ

Why does nextjs-client-cookie-pattern use server actions?

nextjs-client-cookie-pattern routes cookie writes through server actions because client components cannot safely call next/headers cookies(); the server action becomes the bridge after user clicks or form submissions.

What user interactions does nextjs-client-cookie-pattern cover?

nextjs-client-cookie-pattern covers client onClick handlers and form submissions that invoke server actions to set authentication tokens, user preferences, or session values as server-side cookies.

Which Next.js APIs does nextjs-client-cookie-pattern rely on?

nextjs-client-cookie-pattern pairs React client components with server actions and the next/headers cookies() helper inside the App Router architecture for secure HttpOnly cookie mutation.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.