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: { title: 'Card', group: 'common', 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

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.

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.

Multilingual sites

A multilingual site needs very little from a frontend. Render each language's tree as it comes, offer the page in the site's other languages, and the editor's translation tools work through the frontend you already have.

Advanced

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