Compare and switch
When Ginko fits, and when Nuxt Content, a hosted CMS or a Git-based editor fits better. What each API of Nuxt Content and @nuxtjs/i18n becomes in Ginko.
Ginko or Nuxt Content
Both read Markdown from content/ and render it in a Nuxt site. They solve different jobs.
Take Ginko when people who are not developers edit the site. The same brick can come from files today and from the platform tomorrow: the site code stays the same. Owners edit in the Studio, with approval, a schedule, history, and a link check before anything goes live. Languages, addresses per language, and hreflang come with it.
Take Nuxt Content when only developers edit, in Git, and you need what Ginko does not have: Nuxt Studio, or more than 500 entries in one collection (Ginko's limit). Ginko has data files in YAML, JSON and CSV and queries with where, order and limit too.
A site can also start with Ginko file bricks only. It then needs no platform and no account.
Ginko, a hosted CMS or a Git-based editor
Owners can also edit a Nuxt site with a hosted CMS, such as Storyblok or Contentful, or with an editor that writes to Git, such as Decap CMS, TinaCMS or Nuxt Studio. Each fits a different team.
| Ginko | Hosted CMS | Git-based editor | |
|---|---|---|---|
| Where the content model lives | in code, ginko.config.ts | in the CMS | in code or in the editor's config |
| Where the content lives | on the Ginko platform, or as files in the repo | in the CMS | as files in the repo |
| What a change does | platform content goes live without a new build, after approval if the brick asks for it; file content needs a build | depends on the CMS and the site setup | becomes a commit and needs a new build |
| Frameworks | Nuxt only | most frameworks | depends on the editor |
| Access | the platform is open by invitation | sign-up | depends on the editor |
Take a hosted CMS when editors build pages from blocks themselves, when many sites in different frameworks share one content store, or when you need its integrations.
Take a Git-based editor when every change should be a commit and the content should never leave the repo.
Take Ginko when the site is Nuxt, a developer defines what owners may change, and owners change it often: opening hours, a menu, events.
When not to use Ginko
- The site is not built with Nuxt.
- Editors should arrange pages from sections themselves. Ginko is not a page builder.
- One collection needs more than 500 entries.
- The content almost never changes. Plain Nuxt is enough.
- You need a platform that you can sign up for today. Lupinum opens the Ginko platform by invitation. Without it, you can still use file bricks.
Leaving the platform
ginko export saves the schema, all entries and all files of a site to a folder: export.json plus files/. The folder is a backup in Ginko's own format, not the file layout of content/. To move a brick to files, you convert the entries yourself. See Platform and CLI.
From Nuxt Content
| Nuxt Content | Ginko |
|---|---|
content.config.ts with defineCollection | ginko.config.ts with collection({ source: 'files', … }) |
a schema with Zod | fields with f.*: f.text, f.markdown, f.image, … |
queryCollection('docs').path(path).first() | useGinkoEntry('docs', slug) |
queryCollection('blog').where(…).order(…).all() | useGinkoQuery('blog', { where, order, limit }) |
queryCollectionNavigation | useGinkoNav(['docs']) |
queryCollectionItemSurroundings | useGinkoNeighbors('docs', id) |
queryCollectionSearchSections | the search index at /__ginko/search/<locale>.json |
<ContentRenderer :value="page" /> | <GinkoMarkdown :value="doc.data.body" /> |
MDC ::callout | the same syntax; you map names to components with :components |
useSeoMeta per page | useGinkoSeo({ … }) |
What changes when you move:
- One file per entry and language.
1.einstieg.mdis the main locale,1.einstieg.en.mdthe translation. See Content from files. - Frontmatter is checked. A key the brick does not know stops the build, with the file and the field in the message.
- Queries read the bundle.
useGinkoQueryruns on the data of the page, not on a database.wheretakeseq,ne,gt,gte,lt,lte,in, andcontains;orderis a text like'date desc'or an object like{ field: 'date', dir: 'desc' }. - Links between entries follow the language. In Markdown,
[Bricks]($docs.bausteine)points to the entry in the language of the page. A link that points nowhere stops the build.
From @nuxtjs/i18n
The Ginko module installs ginko-i18n and gives it the locales of the site. Remove @nuxtjs/i18n: setup stops with a message when both are installed.
| @nuxtjs/i18n | ginko-i18n |
|---|---|
i18n: { locales, defaultLocale } in nuxt.config.ts | locales in ginko.config.ts; the first one is the main locale |
i18n: { strategy } in nuxt.config.ts | the same, in nuxt.config.ts |
$t('key'), useI18n().t | useI18n().t |
useLocalePath() | useI18n().localePath |
useSwitchLocalePath(), useLocaleHead() | useI18n().alternates; head tags are set for you |
defineI18nRoute({ paths }) | definePageMeta({ i18n: { paths } }) |
i18n/locales/*.json with vue-i18n syntax | i18n/<code>.json with ICU syntax |
detectBrowserLanguage | useLocaleSuggestion(): a link the visitor can choose |
What changes when you move:
- Plurals use ICU.
'no dish | one dish | {count} dishes'becomes'{count, plural, =0 {no dish} one {# dish} other {# dishes}}'. - Two strategies.
prefix_except_default(the default) andprefix. Domains per language andno_prefixdo not exist. - The address decides the language. No cookie and no browser header switches a page. For apps behind a login,
userLocalelets chosen paths follow the user instead. - Detail pages need no extra code. The addresses of an entry in each language come from the bundle, so the language switcher and hreflang are right without
setI18nParams.
The details are in Languages.