Client API
import { useCopy, CopyInkProvider } from 'copy-ink/client'
useCopy is also exported from copy-ink, but that entry carries the Server
Components too. Importing from copy-ink/client guarantees a client component
pulls in nothing but the hook, without relying on the bundler to tree-shake.
useCopy()
'use client'
import { useCopy } from 'copy-ink/client'
export function Hero() {
const copy = useCopy()
return (
<img
src="/hero.png"
alt={copy.get('heroImg.alt')}
title={copy.get('heroImg.title')}
/>
)
}
Returns a CopyAccessor — the server's CopyReader minus all():
| Member | Type | Description |
|---|---|---|
get(field) | string | Coerced; '' when missing |
raw(field) | unknown | The parsed value |
has(field) | boolean | |
scope | string | |
locale | string |
Throws when no provider is in scope. That is deliberate: silently returning empty strings would look like missing content rather than missing wiring.
<CopyProvider>
The provider you actually render is the server one, which reads content and hands it down:
import { CopyProvider } from 'copy-ink/server'
import { Hero } from './Hero'
export default function Page() {
return (
<CopyProvider>
<Hero />
</CopyProvider>
)
}
Put it in the page, not the root layout, when it should follow navigation — App Router does not re-render a layout that did not change, so a layout-level provider keeps serving the first route's content.
Its props are documented in the component reference.
<CopyInkProvider>
import { CopyInkProvider } from 'copy-ink/client'
<CopyInkProvider value={{ scope: 'about', locale: 'en', data }}>
{children}
</CopyInkProvider>
The underlying client-side context provider. Use it when you already have the
content — a test, a Storybook story, a client-rendered preview — and want to
skip the server round trip. <CopyProvider> is a thin wrapper around it.
| Type | Shape |
|---|---|
CopySnapshot | { scope: string; locale: string; data: ContentData } |
What this costs
The provider serialises one scope's whole document into the RSC payload. That is the trade for reading content on the client at all.
Keep it to pages that need it, and prefer passing a single string as a prop when that is all a client component wants:
// Server Component
<Hero alt={copy.get('heroImg.alt')} />