Other Frameworks
Kitsune’s metadata protocol is plain DOM attributes. The runtime listens at <kit-boundary>, which is a custom element that works in any framework. Most integrations are zero-line.
The minimum: any framework
Section titled “The minimum: any framework”<kit-shell name="my-app"> <kit-boundary surface="page"> <button data-meta-event="checkout.started">Pay</button> </kit-boundary></kit-shell>
<script type="module"> import '@atheory-ai/kitsune-app' import { defineKitModule } from '@atheory-ai/kitsune-core'
await customElements.whenDefined('kit-shell') const shell = document.querySelector('kit-shell') shell.modules = [ defineKitModule({ name: 'analytics', events: { '*': (e) => console.log(e.type, e.context, e.payload) }, }), ]</script>Whatever rendered the <button> doesn’t matter. The boundary parses it on click. This is the integration story for every framework: render through your tooling, attach metadata, the runtime takes over.
Custom elements need to be told they’re not Vue components. Add to your Vue config:
import vue from '@vitejs/plugin-vue'
export default { plugins: [ vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('kit-') } }, }), ],}Then:
<template> <kit-shell name="vue-app"> <kit-boundary :surface="surface" :feature="feature"> <button data-meta-event="thing.happened" @click="onClick">Click</button> </kit-boundary> </kit-shell></template>
<script setup>import '@atheory-ai/kitsune-app'import '@atheory-ai/kitsune-ui'import { onMounted } from 'vue'
onMounted(async () => { await customElements.whenDefined('kit-shell') document.querySelector('kit-shell').modules = [/* modules */]})</script>A Vue-native adapter is on the roadmap. Until then, the above pattern works.
Svelte
Section titled “Svelte”Svelte 5 has full custom-element support. No special configuration needed.
<script> import '@atheory-ai/kitsune-app' import '@atheory-ai/kitsune-ui' import { onMount } from 'svelte'
onMount(async () => { await customElements.whenDefined('kit-shell') document.querySelector('kit-shell').modules = [/* ... */] })</script>
<kit-shell name="svelte-app"> <kit-boundary surface="page"> <button data-meta-event="thing.happened">Click</button> </kit-boundary></kit-shell>A Svelte-native adapter (with stores instead of hooks) is on the roadmap.
import '@atheory-ai/kitsune-app'
export function App() { return ( <kit-shell name="solid-app" ref={async (shell) => { await customElements.whenDefined('kit-shell') shell.modules = [/* ... */] }}> <kit-boundary surface="page"> <button data-meta-event="thing.happened">Click</button> </kit-boundary> </kit-shell> )}Solid’s fine-grained reactivity composes well with custom elements. Most patterns work without an adapter.
Next.js
Section titled “Next.js”Next.js apps can use Kitsune in two ways:
Client components. Mark your component 'use client', render <kit-shell> inside, install modules in useEffect.
Server components for layout, client islands for interaction. The shell is client-side. Render the layout (boundaries, content) on the server, hydrate one or two interactive islands.
Either way, register custom elements only on the client:
'use client'
import { useEffect } from 'react'
export function KitsuneRoot({ children }: { children: React.ReactNode }) { useEffect(() => { Promise.all([ import('@atheory-ai/kitsune-app'), import('@atheory-ai/kitsune-ui'), ]).then(async () => { await customElements.whenDefined('kit-shell') const shell = document.querySelector('kit-shell')! shell.modules = [/* ... */] }) }, [])
return <kit-shell name="app">{children}</kit-shell>}For React-specific helpers, the React adapter provides KitShellProvider and KitBoundary as a cleaner alternative to raw custom elements. Both work in Next.js.
Same idea as Next.js but with Vue. Configure custom-element compilation, register on client only via nuxt.config.ts plugin:
export default defineNuxtPlugin(async () => { await import('@atheory-ai/kitsune-app') await import('@atheory-ai/kitsune-ui')})Then use <kit-shell> and <kit-boundary> in any component.
TanStack Start, Astro, Remix
Section titled “TanStack Start, Astro, Remix”The pattern is the same:
- Register custom elements client-side only.
- Wrap the layout in
<kit-shell>and boundaries. - Install modules in a client effect.
The runtime is portable. Anything that ships HTML and JS to a browser can host it.
When you need an adapter
Section titled “When you need an adapter”You need an adapter when you want:
- Hooks-style ergonomics in your framework’s idiom (
useKitEmit()instead ofruntime.emit()). - Type-safe boundaries as framework components.
- SSR awareness (e.g., emit nothing during server render).
Until then, custom elements + data-meta-* is plenty.
What’s next
Section titled “What’s next”- React — the deepest existing adapter
- Adopting Incrementally — practical migration
- The Metadata Protocol — what the boundary parses