njin

Drafts and languages

Vars groups support the same two options as models: drafts and languages. They work together or on their own.

const general = makeVars("general", {
  name: "General",
  i18n: true,
  draftable: true,
  schema: z.object({
    siteName: text({ label: "Site name" }, (z) => z.default("My Site")),
    tagline: text({ label: "Tagline", localized: true }, (z) => z.default("")),
  }),
});

Localized vars

i18n: true needs i18n set in config.ts (see Languages) and at least one field with localized: true. A relation field cannot be localized. It follows the language of the model it points at.

Write a localized field as an object keyed by language. A language you leave out is untouched:

await general.update({ tagline: { en: "Welcome" } });

Read one language, flat, with locale. A language that has no value of its own falls back to the default language. Pass fallback: false to turn that off:

await general.get({ locale: "en" });
// { siteName: "Acme", tagline: "Welcome", updatedAt: "...", ... }

Without locale, get() returns every language, and a localized field is an object: tagline: { id: "", en: "Welcome" }. This is what the admin panel edits.

Draftable vars

draftable: true gives the group a live copy next to the working copy, as in Draft and publish.

Method What it does
group.get() The working copy, which is what the admin panel edits
group.published.get() What is live
group.publish() Copy the working copy to the live copy
group.unpublish() Take the live copy offline
group.discard() Throw away unpublished edits

Use published.get() in public templates. Nothing is live until the first publish(), so before that it returns the .default() of every field:

@let(site = await general.published.get({ locale: 'en' }))
<title>{{ site.siteName }}</title>

On a draftable group, get() also returns status, which is "draft", "published" or "changed", and publishedAt. Do not name a field status or publishedAt on a draftable group.

Languages and drafts together

With both options, each language is published on its own. publish(), unpublish() and discard() take a locale: one code, a list of codes, or nothing for every language.

await general.publish({ locale: "en" });

status is then an object with one entry per language: { id: "draft", en: "published" }.

API

Request What it does
GET /api/vars/{prefix}?locale=en One language, flat. Add fallback=false to turn off the fallback
GET /api/vars/{prefix}/published The live copy. Also takes locale
POST /api/vars/{prefix}/publish Publish. Also /unpublish and /discard
POST /api/vars/{prefix}/publish?locale=en Publish only some languages: en or en,id

Hooks

afterVarsPublish(group, fn) and afterVarsUnpublish(group, fn) run after a publish or unpublish, with the group's values. See Hooks.