
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-patternAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 287 |
|---|---|
| repo stars | ★ 109 |
| Last updated | October 24, 2025 |
| Repository | wsimmonds/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
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()fromnext/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 CookieButtonTypeScript: 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
cookiesfromnext/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.