Frontend developer guide

Inka makes an existing frontend editable. You add a small script and a few HTML attributes, and editors compose pages inside the bounds your design system allows — no React or Vue required in the frontend itself. This guide takes you from a running page to custom blocks, live preview, and templates.

Quick start

Add Inka to a page in your framework. Each tab is a real, working starter — the same source the test suite runs against.

<!-- pages/[...slug].vue -->
<template>
  <div v-for="id in page?.blocks_layout?.items" :key="id"
       :data-block-uid="editing ? id : undefined">
    <!-- The card block is rendered editable; anything else is shown as raw JSON. -->
    <a v-if="page.blocks[id]['@type'] === 'card'"
       :href="page.blocks[id].link"
       :data-edit-link="editing ? 'link' : undefined">
      <!-- data-edit-media: click to pick/upload image · data-edit-text: edit text in place -->
      <img :src="page.blocks[id].image" :data-edit-media="editing ? 'image' : undefined" />
      <h3 :data-edit-text="editing ? 'title' : undefined">{{ page.blocks[id].title }}</h3>
      <p :data-edit-text="editing ? 'description' : undefined">{{ page.blocks[id].description }}</p>
    </a>
    <pre v-else>{{ JSON.stringify(page.blocks[id], null, 2) }}</pre>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { initBridge } from '@hydra-js/hydra.js'

const page = ref(null)
const editing = ref(false)

onMounted(async () => {
  // Only init the bridge when loaded inside the editor.
  if (window.name.startsWith('hydra')) {
    editing.value = true
    initBridge({
      // Declare the one block type we render richly.
      blocks: { card: { blockSchema: { properties: {
        image: { widget: 'image' },
        title: { type: 'string' },
        description: { type: 'string' },
        link: { widget: 'url' },
      } } } },
      // Re-render on every edit.
      onEditChange: (data) => { page.value = data },
    })
  } else {
    // On the live site: fetch this page and render once.
    page.value = await (await fetch(`/++api++${useRoute().path}`)).json()
  }
})
</script>

Next: Installation for the full setup, then Build a frontend for framework specifics.

In this guide

Advanced

Advanced topics for optimising your Inka integration: lazy loading the bridge, authentication, and preventing reloads.

Templates & Layouts

Templates allow editors to centrally control content and reuse content. They allow a developer to not have to hard code layout decisions and instead use rules to apply user layouts in template content stored separately from the page, or give the user a choice on which layout they want.

Listings & Dynamic Blocks

A listing block fetches content from the server (e.g. latest news) and renders each result as a separate block, repeating each block once per result entry. This means a listing can be moved between containers and reuse normal blocks for what it repeats.

Container Blocks

A block — or the page itself — is divided into regions, and each region holds an ordered list of blocks. Sliders have a slides region, grids have columns, accordions have panels; a page has its main items region (and optionally a header, footer, …).

Custom Blocks

Define custom block types directly in your frontend configuration via the blocks option in initBridge. No Volto plugin deployment required. Each block type needs an id, title, and a blockSchema with its field properties.