Skip to content

🍫 Best practice Nuxt and KQL starter for your headless Kirby CMS

License

Notifications You must be signed in to change notification settings

johannschopplich/cacao-kit-frontend

Repository files navigation

Cacao Kit Frontend

Cacao Kit (Frontend)

A production-ready Nuxt starter kit for headless Kirby CMS.

Usage β€’ Architecture β€’ Deployment

Tip

If internationalization is not a requirement for your project, check out the 🧱 branch without Nuxt i18n.

If this is your first time building an application with Nuxt and Kirby, take a look at the πŸ’š Kirby Nuxt Starterkit first to get a basic understanding of this tech stack.

This is a minimal but feature-rich Nuxt starter kit and the evolved version of the Kirby Nuxt Starterkit. It pairs with the 🍫 Cacao Kit backend.

Key Features

  • πŸ† Motto: "Everything is a block" – Kirby blocks define what to render for each page
  • πŸ›£οΈ All pages are rendered by the catch-all route by default (you can still create Nuxt pages)
  • 🌌 Use Kirby's page structure as the source of truth
  • πŸ«‚ Kirby Query Language with nuxt-kirby
  • 🌐 Internationalization with @nuxtjs/i18n
  • πŸ› Global site data similar to Kirby's $site
  • πŸ”Ž SSR-generated SEO data
  • πŸ“ Prettier & ESLint
  • πŸ”’ Pre-configured VSCode settings

The block-first approach is a core design decision: use Kirby's page structure as the source of truth without replicating it in Nuxt. All pages are rendered by the catch-all route.

If you need custom Kirby page blueprints with custom fields, you can create dedicated Nuxt pages and query the content using KQL. See pages/about.vue for an example.

Usage

Prerequisites

  1. Enable Corepack using corepack enable
  2. Install dependencies using pnpm install
  3. Adapt the relevant environment variables:
# Base URL of the Kirby backend
KIRBY_BASE_URL=
# Token for bearer authentication
# See https://github.com/johannschopplich/cacao-kit-backend#bearer-token
KIRBY_API_TOKEN=

Development

  1. Start the development server using pnpm run dev
  2. Visit localhost:3000

Production

Build the application for production with pnpm run build.

Check out the deployment documentation.

Architecture & Development

Project Structure

The Cacao Kit follows a clear architectural pattern designed around its block-first approach:

app/
β”œβ”€β”€ components/
β”‚   └── Kirby/
β”‚       β”œβ”€β”€ Block/           # Individual block components
β”‚       β”œβ”€β”€ Blocks.vue       # Block renderer
β”‚       └── Layouts.vue      # Layout renderer
β”œβ”€β”€ composables/
β”‚   β”œβ”€β”€ links.ts             # Internal link handling
β”‚   └── proxy.ts             # Development proxy utilities
β”œβ”€β”€ pages/
β”‚   β”œβ”€β”€ [...slug].vue        # Universal page renderer
β”‚   └── about.vue            # Custom page example
β”œβ”€β”€ plugins/
β”‚   └── site.ts              # Global site data management
└── queries/                 # KQL query definitions
    β”œβ”€β”€ index.ts
    β”œβ”€β”€ page.ts
    β”œβ”€β”€ site.ts
    └── prefetch.ts

Block-First Architecture

Every page is rendered through the catch-all route [...slug].vue, which dynamically renders either:

  • Layouts: Column-based content using KirbyLayouts
  • Blocks: Linear content using KirbyBlocks
<template>
  <div>
    <KirbyLayouts v-if="page?.layouts?.length" :layouts="page.layouts" />
    <KirbyBlocks v-else-if="page?.blocks" :blocks="page.blocks" />
  </div>
</template>

Adding New Blocks

  1. Create the block component in app/components/Kirby/Block/:
<!-- app/components/Kirby/Block/MyCustomBlock.vue -->
<script setup lang="ts">
import type { KirbyBlock } from '#nuxt-kirby'

defineProps<{
  block: KirbyBlock<'my-custom-block'>
}>()
</script>

<template>
  <section class="my-custom-block">
    <h2>{{ block.content.title }}</h2>
    <div v-html="block.content.text" />
  </section>
</template>
  1. Register the block in app/components/Kirby/Blocks.vue:
import { LazyKirbyBlockMyCustomBlock } from '#components'

const blockComponents: Record<string, Component> = {
  // Custom blocks
  'my-custom-block': LazyKirbyBlockMyCustomBlock,
}

Working with KQL Queries

Define reusable queries in the queries/ directory:

// app/queries/blog.ts
import type { KirbyQuerySchema } from 'kirby-types'

export const blogQuery: KirbyQuerySchema = {
  query: 'page("blog")',
  select: {
    title: true,
    children: {
      query: 'page.children.listed',
      select: {
        title: true,
        date: true,
        excerpt: 'page.text.excerpt(300)',
        cover: {
          query: 'page.cover.toFile?.resize(600)',
          select: ['url', 'alt'],
        },
      },
    },
  },
}

Use them in components:

<script setup lang="ts">
import { blogQuery } from '~/queries/blog'

const { locale } = useI18n()
const { data } = await useKql(blogQuery, {
  language: locale.value,
})
</script>

Internationalization

The kit includes full i18n support with @nuxtjs/i18n.

Custom Styling

This kit uses semantic HTML with minimal styling via new.css for demonstration. To implement your own styling, remove the import in app.vue and add your custom styles.

Deployment

Static Site Generation

For maximum performance and CDN compatibility, generate a static site:

pnpm run generate

This creates a fully static version in the dist/ directory that can be hosted on any static hosting service.

Server-Side Rendering

Deploy with full SSR capabilities:

pnpm run build

Environment Configuration

Ensure these environment variables are set in production:

# Required: Your Kirby backend URL
KIRBY_BASE_URL=https://your-kirby-backend.com

# Required: Authentication token for KQL queries
KIRBY_API_TOKEN=your-secret-token

# Optional: Public site URL for SEO and social sharing
NUXT_PUBLIC_SITE_URL=https://your-frontend.com

Preview

What's Kirby?
  • getkirby.com – Get to know the CMS.
  • Try it – Take a test ride with our online demo. Or download one of our kits to get started.
  • Documentation – Read the official guide, reference and cookbook recipes.
  • Issues – Report bugs and other problems.
  • Feedback – You have an idea for Kirby? Share it.
  • Forum – Whenever you get stuck, don't hesitate to reach out for questions and support.
  • Discord – Hang out and meet the community.
  • YouTube – Watch the latest video tutorials with Bastian.
  • Mastodon – Spread the word.
  • Instagram – Share your creations: #madewithkirby.

License

MIT License Β© 2023-PRESENT Johann Schopplich

About

🍫 Best practice Nuxt and KQL starter for your headless Kirby CMS

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors