Code Sync — preview your own site

Most headless CMSs preview content by rendering it themselves, which means the preview is always an approximation. Ondros doesn't. Connect your GitHub repository and the editor loads your own deployed site, then edits it in place — the same arrangement as Adobe's Universal Editor.

Authors see the real page: your CSS, your components, your layout.

Until a space is connected, the preview pane says Connect with GitHub for preview rather than showing an approximation.

Connect a repository

  1. Settings → Code Sync → Connect with GitHub.
  2. Install the Ondros Code Sync app on the repository that builds your site, granting read access to its contents.
  3. Pick the repository and branch. Ondros reads your component mapping immediately and reports what it found.

Pushes to that branch re-sync the mapping automatically. Removing the installation disconnects the space; your content is untouched.

Your repo needs almost nothing

1. The bridge script

One tag, served by Ondros so every site runs the same version:

<script src="https://your-cms.example.com/code-sync/ondros-editor.js" defer></script>

It no-ops unless the page is open inside the editor, so it is safe to ship to production.

2. Tell the editor what each element renders

<section data-ondros-resource="entry:9f2c…" data-ondros-component="hero">
  <h1 data-ondros-prop="heading"    data-ondros-type="text">Ship faster</h1>
  <p  data-ondros-prop="subheading" data-ondros-type="text">One place …</p>
</section>
AttributeMeaning
data-ondros-resourceentry:<uuid> — which entry this subtree renders
data-ondros-componentthe content type's api id
data-ondros-propthe field id
data-ondros-typetext · longtext · richtext · select · number · media · reference
data-ondros-labeloptional label shown in the editor's overlay

Nest the wrappers freely. A landing page that renders a hero and three cards emits one data-ondros-resource per block, and every edit is attributed to the right entry automatically.

Text fields become editable on double-click. Structured values — references, media, JSON — are edited in the form, because there's no sensible in-page representation of them.

3. A manifest, only if you need one

A repository with no manifest is mapped by convention: components are discovered from its blocks/ directory and bound to the content type of the same name, with every page routed at /{contentType}/{slug}. Connect and preview — commit nothing.

Commit ondros/component-definition.json when your project doesn't match that:

{
  "previewUrl": "https://your-site.example.com",
  "routes": {
    "landing_page": "/{slug}",
    "article": "/blog/{slug}"
  },
  "components": [
    {
      "id": "hero",
      "title": "Hero Section",
      "contentType": "hero",
      "block": "hero",
      "fields": [
        { "name": "heading", "label": "Heading", "component": "text" }
      ]
    }
  ]
}

Already using Adobe's Universal Editor? A repo with a component-definition.json (and optional component-models.json) in AEM's shape connects unchanged. A component's id binds to the content type with the same api id; override that, and add routes or a preview URL, under plugins.ondros.

Page-wise and component-wise preview

Whether an entry is a page or a block is decided by your content model: a type is addressable because it has a slug field.

EntryWhat the editor shows
Type has a slug field, filled inThe entry's own URL on your site
Type has a slug field, still blank"Fill in the slug to give this entry a URL"
Type has no slug field, used on a pageThat page, scrolled to the component and outlined
Type has no slug field, unused"No page references this yet"

That last pair is what makes editing a hero or card sensible. A block has no page of its own, so Ondros finds a page that references it — published pages preferred — and reveals it there.

Draft previews

The editor previews drafts, so your site should fetch with a preview key (cms_pre_…) and skip its cache when the page is loaded with ?ondros-preview=1. Ondros also passes:

ParameterMeaning
ondros-preview=1render drafts, uncached
ondros-locale=<code>the locale the editor has active
ondros-focus=<uuid>the block to scroll to and outline

Without a preview key the editor shows published content, and an author's unsaved edits appear to do nothing.

Troubleshooting

SymptomCause
"Connect with GitHub for preview"The space has no repository connected
"Code Sync isn't set up on this server"Self-hosting without the GitHub App configured
Page loads but nothing is selectableMissing data-ondros-* attributes — the editor says so explicitly
"No page references this … yet"A block no page uses; add it to a page
"This entry has no URL yet"A page whose slug field is still blank
Edits don't appearThe site isn't using a preview key for ?ondros-preview=1

Start from the reference project

ondros-demo-site is a small Next.js site wired up exactly this way — manifest, bridge script, instrumented components and draft previews. It is the fastest way to see the whole loop working, and the easiest thing to copy from.

Try it end to end

  1. Fork or clone it.

    git clone https://github.com/proteendas/ondros-demo-site
    cd ondros-demo-site && npm install
    
  2. Point it at your space. Copy .env.example to .env.local and fill in the space id and both tokens from Settings → API keys:

    CMS_URL=https://your-cms.example.com
    NEXT_PUBLIC_CMS_URL=https://your-cms.example.com
    CMS_SPACE_ID=…
    CMS_ENVIRONMENT=master
    CMS_DELIVERY_TOKEN=cms_del_…
    CMS_PREVIEW_TOKEN=cms_pre_…
    

    The preview token is the one people forget. Without it the editor shows published content, and an author's unsaved edits look like they did nothing.

  3. Give it content to render. One command builds the whole model and fills it with copy, so there is nothing to click together first:

    CMS_SPACE_ID=… CMS_MANAGEMENT_TOKEN=cms_mgm_… \
      node scripts/seed-content.mjs
    

    That creates four content types and eight entries — a landing_page with slug home referencing a hero and three card blocks, plus three articles — written in English and French so the language switcher has something to show. --draft leaves it unpublished; --dry-run just prints the plan. Re-running tops up rather than duplicating.

    The copy lives in content/demo-content.json, so you can rewrite it for your own pitch before seeding.

  4. Deploy it, or run npm run dev and expose it with a tunnel. The editor loads this URL in an iframe, so it has to be reachable from the browser — localhost works when you're running the CMS locally too.

  5. Connect it. In the editor: Settings → Code Sync, install the app on the repository, pick the branch, and set the deployment URL.

  6. Open any entry. The preview pane is now the demo site. Hover a heading to see it outlined, click to jump to its field, double-click to edit in place. Open a hero or card and it previews inside the page that uses it.

Copying it into an existing project

Four things to port, each isolated to one place in the demo:

WhatWhere it lives in the demo
Manifest — routes and component mappingondros/component-definition.json
Bridge script tagsrc/app/layout.tsx
resource() / prop() attribute helperssrc/lib/preview.ts
Draft fetching with the preview tokensrc/lib/cms.ts

Then instrument your components: wrap each entry in resource() and mark its fields with prop(), as src/app/page.tsx does for the hero and cards.

The manifest is worth reading closely, because it shows why you would commit one at all. The demo's routes don't follow the default /{contentType}/{slug} convention — articles live at /articles/{slug} and landing pages render at / — so it declares them:

{
  "routes": {
    "landing_page": "/?ondros-slug={slug}",
    "article": "/articles/{slug}"
  }
}

If your routes do follow the convention, delete the file and let Ondros infer the mapping.

Not a Next.js project?

Nothing here is Next-specific. The bridge script is plain JavaScript and the attributes are plain HTML, so the same four pieces apply to Astro, Nuxt, SvelteKit, Remix, Eleventy or a hand-rolled server — anything that can render an attribute and include a script tag.