Images
<Copy.Image field="aboutImg" />
<Copy.Image> wraps next/image, pulling src, alt, width and height
from the typed node:
aboutImg:
_type: image
src: "hero.png"
alt: "The team at work"
width: 1200
height: 800
It also accepts className, sizes, priority, quality and loading, which
pass straight through to next/image.
Where files live
Images live only in public/content/…, mirroring the content tree:
content/about/_index.yml → public/content/about/hero.png
content/contact.yml → public/content/contact/map.png
The natural assumption is that images sit next to the .yml. They do not: Next
only serves static files from public/, and mirroring avoids both a route
handler and a copy step at build time.
A src without a scheme is relative to that mirror directory. A src starting
with / is treated as an absolute public path and used as-is.
Locale fallback
A relative src resolves against the current locale's mirror and falls back to
the default locale's when the file is absent there:
public/content/de/about/hero.png ← used if present
public/content/en/about/hero.png ← otherwise
So a translator overrides an image only where it actually differs. alt still
comes from that locale's own YAML, so the wording translates independently of
the file.
An image uploaded while editing a non-default locale is an override by definition, and the editor says so before saving it.
Remote sources
A remote src must come from a host in images.allowedHosts:
images: {
allowedHosts: ['cdn.example.com', '*.imgix.net'],
}
A leading *. matches one level of subdomain.
The editor rejects an undeclared host at paste time. Hosts are never auto-appended when one shows up in content: that would turn the Next image optimizer into an open proxy anyone could use to fetch and cache images on your bandwidth.
If a remote source from an undeclared host does end up in a content file, the
component renders a plain <img unoptimized> rather than throwing. An
undeclared host is a config gap, not a reason to break the page — but
copy-ink check will tell you about it.
Wiring next.config
next/image needs the same host list, and it reads it at process start:
import hosts from './.copy-ink/image-hosts.json' with { type: 'json' }
export default {
images: {
remotePatterns: hosts.map((hostname) => ({ protocol: 'https', hostname })),
},
}
npx copy-ink types regenerates image-hosts.json from your config. Because
next.config is read once at start-up, next dev needs a restart when
allowedHosts changes. copy-ink doctor warns when next.config does not
import the file.
Uploads
The editor uploads into the mirror directory for the field's scope and locale,
and the file lands in the same commit as the text changes. Uploads over
images.maxUploadBytes (8 MB by default) are rejected with a message, not
silently truncated.
The editor checks the size and the host before uploading, so the client gets a useful message — and the route handler checks both again before committing, along with the destination path. The browser's copy of a rule is a courtesy; the server's copy is the rule.
Every uploaded byte is a byte in your git history — repositories do not forget. For a site with heavy image churn, point the client at a CDN host instead and let them paste URLs.
Orphans
npx copy-ink prune # list images no content file references
npx copy-ink prune --delete # remove them
Deleting an item or replacing an image leaves the old file behind, because
nothing else can safely decide it is unreferenced. prune is that decision,
made deliberately.