Nuxt · Nuxt 3 and later
Visual editing for Nuxt, on WordPress.
A client plugin loads the bridge, a server route serves each page, and v-bind writes the attributes on your elements.
- 01
Allow the editor to display your site
Copy this into nuxt.config.ts.
nuxt.config.ts export default defineNuxtConfig({ routeRules: { '/**': { headers: { 'Content-Security-Policy': "frame-ancestors 'self' https://cms.example.com" }, }, }, }); - 02
Add the bridge
Install the package, then create this client plugin.
Terminal npm install leihoplugins/leiho.client.ts export default defineNuxtPlugin(() => { if (window.self === window.top) return; import('leiho').then(({ initLeihoBridge }) => { // Already on screen: don't hide the page. initLeihoBridge({ wpOrigin: 'https://cms.example.com', cloakTimeout: 0 }); }); }); - 03
Read the content
Serve each page from a server route, then fetch it in your page.
server/api/page.get.ts import { loadLeihoContent, indexLeihoContent } from 'leiho/content'; export default defineEventHandler(async (event) => { const { path = '/' } = getQuery(event); const { data } = await loadLeihoContent({ url: 'https://cms.example.com/wp-json/leiho/v1/export' }); const page = indexLeihoContent(data).byPath(String(path)); if (!page) throw createError({ statusCode: 404 }); return page; });pages/[...slug].vue <script setup lang="ts"> const route = useRoute(); const { data: page } = await useFetch('/api/page', { query: { path: route.path } }); </script> <template> <main v-if="page" :data-wp-post="page.id"> <h1 data-wp-field="_title">{{ page.title }}</h1> </main> </template>Serverless host with a read-only disk? Pass snapshot: false to loadLeihoContent.
- 04
Mark what editors can change
Spread field() on each element editors should change. Always render the element, even when its value is empty.
Vue <script setup> import { field, entry } from 'leiho'; // field() writes data-wp-post + data-wp-field, entry() names a section. // Flexible content rows: block(page, 'sections', i, 'Label'). </script> <template> <section v-bind="entry(page, 'Hero')"> <h1 v-bind="field(page, 'hero_title')">{{ page.fields.hero_title }}</h1> <img v-bind="field(page, 'hero_image')" :src="page.fields.hero_image?.url" :alt="page.fields.hero_image?.alt"> <article v-for="(card, i) in page.fields.cards" :key="i" v-bind="field(page, `cards.${i}.title`)"> {{ card.title }} </article> </section> </template>
Replace https://cms.example.com with your WordPress address. In wp-admin, Leiho → Get started shows these steps with your address filled in, checks each one, and can give the whole setup to your coding agent as one prompt.
Edit the real site.
Free and open source. Try it on a real WordPress, in your browser.