@lars-plate/delta-client-vue
v1.1.15
Published
Vue 3 components, views, and composables for rendering **Delta** content experiences: building blocks, nested experience components, grid placements, Content Delivery API (CDA) payloads, and iframe communication with the Delta editor.
Readme
@lars-plate/delta-client-vue
Vue 3 components, views, and composables for rendering Delta content experiences: building blocks, nested experience components, grid placements, Content Delivery API (CDA) payloads, and iframe communication with the Delta editor.
Built on @lars-plate/delta-client for types, parsers, the window connector, and CDA helpers.
Features
- Composable tree — Render a full content experience from a root
ExperienceComponent, including nested grid placements - Accessible / CDA tree — Same idea for CDA “accessible” payloads (
AccessibleExperienceComponent) - Dynamic building blocks — Map Delta building-block slugs to your Vue components and receive parsed field data
- Editor integration —
WindowConnectorClienttalks to a parent Delta editor frame (selection, content sync) - Content delivery — Fetch a content experience by URL path (
useDeltaFetchContentExperienceByPath) - Ready-made views — Page-level components for live content, editor iframe, and block showcase
- Customizable rendering — Components expose default markup and scoped slots so you can override layout without forking the library
Requirements
| Package | Version |
|---------|---------|
| vue | ^3.5.13 |
| @tanstack/vue-query | ^5.100.14 (needed for useDeltaFetchContentExperienceByPath) |
| @lars-plate/delta-client | same major as this package |
vue-router is only required if you use DeltaContentExperienceView (it reads useRouter().currentRoute).
Installation
pnpm add @lars-plate/delta-client-vue @lars-plate/delta-client vue @tanstack/vue-query
# or
npm install @lars-plate/delta-client-vue @lars-plate/delta-client vue @tanstack/vue-queryOptional default grid-placement styles (hover outline):
import '@lars-plate/delta-client-vue/index.css'Quick start
1. Register the plugin
// main.ts
import { createApp } from 'vue'
import { VueQueryPlugin } from '@tanstack/vue-query'
import { DeltaClientVue, DELTA_CONFIG } from '@lars-plate/delta-client-vue'
import { Environment, Version, type BuildingBlock } from '@lars-plate/delta-client'
import App from './App.vue'
const app = createApp(App)
app.use(VueQueryPlugin)
app.use(DeltaClientVue, {
blockLoader: async (slug: string): Promise<BuildingBlock> => {
const res = await fetch(`/api/building-blocks/${slug}`)
return res.json()
},
})
app.provide(DELTA_CONFIG, {
environment: Environment.DEV, // 'acc' | 'dev' | 'local' | 'prod'
token: import.meta.env.VITE_DELTA_TOKEN,
version: Version.V1, // 'v0' | 'v1'
})
app.mount('#app')blockLoader is optional but required when a building-block component must load definitions by slug (preview pages, CDA accessible blocks) instead of receiving buildingBlock via props.
Config is injected under the DELTA_CONFIG symbol, not a string key. If it is omitted, composables fall back to import.meta.env.DELTA_ENVIRONMENT, DELTA_TOKEN, and DELTA_VERSION.
| Field | Used by | Description |
|-------|---------|-------------|
| environment | Connector + Content Delivery API | Target Delta stack |
| token | Content Delivery API | Bearer token for API requests |
| version | Content Delivery API | Version segment in the CDA URL |
2. Register your building-block components
Map each building-block slug to a registered Vue component name (string passed to <component :is="...">). Use the DELTA_COMPONENTS symbol:
import { DELTA_COMPONENTS, useDeltaComponents } from '@lars-plate/delta-client-vue'
import HeroBlock from './blocks/HeroBlock.vue'
import TextBlock from './blocks/TextBlock.vue'
const app = getCurrentInstance()!.appContext.app
app.component('HeroBlock', HeroBlock)
app.component('TextBlock', TextBlock)
useDeltaComponents({
'hero-block': 'HeroBlock',
'text-block': 'TextBlock',
})
// Or provide the map yourself:
app.provide(DELTA_COMPONENTS, {
'hero-block': 'HeroBlock',
'text-block': 'TextBlock',
})3. Render a content experience
GraphQL / editor payloads (ExperienceComponent with buildingBlock + fulfillments):
<script setup lang="ts">
import type { ContentExperience } from '@lars-plate/delta-client/graphql'
import { DeltaContentExperienceComponent } from '@lars-plate/delta-client-vue'
defineProps<{
contentExperience: ContentExperience
}>()
</script>
<template>
<DeltaContentExperienceComponent
:content-experience="contentExperience"
:root-experience-component="contentExperience.experienceComponent"
/>
</template>CDA accessible payloads (slug-keyed content, nested components):
<script setup lang="ts">
import type { AccessibleContentExperience } from '@lars-plate/delta-client/parsers'
import { DeltaAccessibleContentExperienceComponent } from '@lars-plate/delta-client-vue'
defineProps<{
contentExperience: AccessibleContentExperience
}>()
</script>
<template>
<DeltaAccessibleContentExperienceComponent
:content-experience="contentExperience"
:root-experience-component="contentExperience.experienceComponent"
/>
</template>Plugin: DeltaClientVue
Registers global components and optional blockLoader:
| Global component | Purpose |
|------------------|---------|
| DeltaContentExperienceComponent | Root wrapper for a GraphQL content experience |
| DeltaExperienceComponentComponent | Single GraphQL experience component + grid children |
| DeltaBuildingBlockComponent | Resolves slug → Vue component + parsed GraphQL data |
| DeltaGridPlacementComponent | Grid cell; recurses into nested experience components |
| DeltaAccessibleContentExperienceComponent | Root wrapper for a CDA accessible experience |
| DeltaAccessibleExperienceComponentComponent | Single accessible component + nested components |
| DeltaAccessibleBuildingBlockComponent | Resolves slug → Vue component + parsed CDA data |
app.use(DeltaClientVue, {
blockLoader?: (slug: string) => Promise<BuildingBlock>
})Provide / inject keys
All shared state uses symbols exported from this package:
| Symbol | Value |
|--------|--------|
| DELTA_CONFIG | { environment?, token?, version? } |
| DELTA_COMPONENTS | Record<slug, vueComponentName> |
| DELTA_BLOCK_LOADER | (slug: string) => Promise<BuildingBlock> (set by the plugin) |
| DELTA_CONNECTOR | WindowConnectorClient (set on first useDeltaClientConnector()) |
| DELTA_ROOT_EXPERIENCE_COMPONENT | Root GraphQL ExperienceComponent (set when isRoot) |
| DELTA_CONTENT_EXPERIENCE_TAGS | Tags from a GraphQL content experience |
| DELTA_CONTENT_EXPERIENCE_TITLE | Title from a GraphQL content experience |
| DELTA_CONTENT_EXPERIENCE_PATH | Path from contentExperience.pathPart.path |
Do not provide('delta-config') or provide('components') — those string keys are not read by this library.
Composables
useDeltaClientConnector()
Returns a WindowConnectorClient for iframe ↔ parent Delta editor communication. Creates and provides the connector on first use.
Requires DELTA_CONFIG.environment (acc | dev | local | prod). The parent origin is environmentEditorUrlMap[environment] from @lars-plate/delta-client (for example https://www.platecms.app in prod, http://localhost:5173 locally).
Example — editor preview iframe
<script setup lang="ts">
import { onUnmounted, ref } from 'vue'
import {
DeltaContentExperienceComponent,
useDeltaClientConnector,
} from '@lars-plate/delta-client-vue'
import { ConnectorEventType } from '@lars-plate/delta-client/connectors'
import type { ContentExperience, ExperienceComponent } from '@lars-plate/delta-client/graphql'
const contentExperience = ref<ContentExperience>()
const rootExperienceComponent = ref<ExperienceComponent>()
const connector = useDeltaClientConnector()
connector.on('message', (event) => {
if (event.type === ConnectorEventType.CONTENT_EXPERIENCE_SEND) {
contentExperience.value = event.payload as ContentExperience
}
if (event.type === ConnectorEventType.ROOT_EXPERIENCE_COMPONENT_SEND) {
rootExperienceComponent.value = event.payload as ExperienceComponent
}
})
onUnmounted(() => {
connector.teardown()
})
</script>
<template>
<DeltaContentExperienceComponent
:content-experience="contentExperience"
:root-experience-component="rootExperienceComponent"
/>
</template>DeltaGridPlacementComponent sends GRID_PLACEMENT_CLICKED through this connector when a placement is clicked (default hover outline in editor mode).
Or use the packaged view: DeltaEditorView.
useDeltaComponents(initialComponents?)
Manages the slug → component name registry (DELTA_COMPONENTS).
const { components, hasComponent } = useDeltaComponents({
'hero-block': 'HeroBlock',
})
if (hasComponent('hero-block')) {
// ...
}useDeltaFetchContentExperienceByPath(path)
TanStack Vue Query wrapper around the Content Delivery API.
Requires VueQueryPlugin and a leading path such as /my-page. Reads environment, token, and version from DELTA_CONFIG (or import.meta.env).
Request: GET {environmentBaseUrlMap[environment]}/{version}/content-experiences/path{path}
Header: Authorization: Bearer {token}
CDA base URLs come from environmentBaseUrlMap in @lars-plate/delta-client (for example https://cda.platecms.app in prod).
Example — route-driven page
<script setup lang="ts">
import { useRoute } from 'vue-router'
import {
DeltaContentExperienceComponent,
useDeltaFetchContentExperienceByPath,
} from '@lars-plate/delta-client-vue'
const route = useRoute()
const { data, isPending, isError, error } = useDeltaFetchContentExperienceByPath(route.path)
</script>
<template>
<div v-if="isPending">Loading…</div>
<div v-else-if="isError">{{ error }}</div>
<DeltaContentExperienceComponent
v-else-if="data"
:content-experience="data"
:root-experience-component="data.experienceComponent"
/>
</template>CDA v1 responses are accessible-shaped. Prefer DeltaAccessibleContentExperienceComponent when version is v1.
Components (GraphQL / editor)
DeltaContentExperienceComponent
Top-level entry for a GraphQL content experience. Provides tags, title, and path to descendants.
| Prop | Type | Description |
|------|------|-------------|
| contentExperience | ContentExperience | Optional; exposed on default slot |
| rootExperienceComponent | ExperienceComponent | Root tree to render |
Default slot props: contentExperience, rootExperienceComponent
Default behavior: renders DeltaExperienceComponentComponent with is-root when rootExperienceComponent is set.
<DeltaContentExperienceComponent
:content-experience="cx"
:root-experience-component="cx.experienceComponent"
>
<template #default="{ rootExperienceComponent }">
<DeltaExperienceComponentComponent
:experience-component="rootExperienceComponent"
is-root
/>
</template>
</DeltaContentExperienceComponent>DeltaExperienceComponentComponent
Renders one GraphQL experience component: building block (if not root) + grid placements.
| Prop | Type | Description |
|------|------|-------------|
| experienceComponent | ExperienceComponent | Required |
| isRoot | boolean | Skips building block when true; provides DELTA_ROOT_EXPERIENCE_COMPONENT |
| Slot | Props | Description |
|------|-------|-------------|
| default | experienceComponent, isRoot | Override building-block area |
| grid-placements | gridPlacements | Override grid rendering |
<DeltaExperienceComponentComponent :experience-component="ec">
<template #grid-placements="{ gridPlacements }">
<div class="my-grid">
<DeltaGridPlacementComponent
v-for="gp in gridPlacements"
:key="gp.prn"
:grid-placement="gp"
/>
</div>
</template>
</DeltaExperienceComponentComponent>DeltaBuildingBlockComponent
Loads (or accepts) a building block, parses field fulfillments with parseDataFromExperienceComponent, and renders your Vue component.
| Prop | Type | Description |
|------|------|-------------|
| buildingBlock | BuildingBlock | Optional; loaded via blockLoader if missing |
| buildingBlockFieldFulfillments | BuildingBlockFieldFulfillment[] | Field values from the experience |
| component | string | Registered component name from useDeltaComponents |
| slug | string | Building-block slug |
| config | ParseDataConfig | Passed to parseDataFromExperienceComponent (default: defaultParseDataConfig) |
| Slot | Props | Description |
|------|-------|-------------|
| default | buildingBlock, data | Full control over render |
| loading | slug | Shown while blockLoader runs |
| not-found | — | Shown when block/data unavailable (default: “Missing building block: {slug}”) |
Example building-block Vue component
<!-- blocks/HeroBlock.vue -->
<script setup lang="ts">
import type { BuildingBlock } from '@lars-plate/delta-client'
import type { Content } from '@platecms/delta-client' // generated by Delta CLI (`delta-types.d.ts`)
defineProps<{
buildingBlock: BuildingBlock
data: Content.HeroBuildingBlock
}>()
</script>
<template>
<section class="hero">
<h1>{{ data.title }}</h1>
<p>{{ data.subtitle }}</p>
</section>
</template>Preview all registered blocks (uses blockLoader):
<script setup lang="ts">
import { DeltaBuildingBlockComponent, useDeltaComponents } from '@lars-plate/delta-client-vue'
const { components } = useDeltaComponents()
</script>
<template>
<DeltaBuildingBlockComponent
v-for="(component, slug) in components"
:key="slug"
:component="component"
:slug="slug"
:config="{ insertPlaceholders: true }"
/>
</template>DeltaGridPlacementComponent
Wraps a grid placement and recursively renders nested DeltaExperienceComponentComponent.
| Prop | Type | Description |
|------|------|-------------|
| gridPlacement | GridPlacement | Required |
Default slot prop: gridPlacement
Clicks emit GRID_PLACEMENT_CLICKED on the connector.
Components (CDA / accessible)
Use these when rendering Content Delivery API v1 payloads (AccessibleContentExperience / AccessibleExperienceComponent). Nested layout is experienceComponent.components, not GraphQL grid placements.
DeltaAccessibleContentExperienceComponent
| Prop | Type | Description |
|------|------|-------------|
| contentExperience | AccessibleContentExperience | Optional; exposed on default slot |
| rootExperienceComponent | AccessibleExperienceComponent | Root tree to render |
Default slot mirrors the GraphQL content-experience component.
DeltaAccessibleExperienceComponentComponent
| Prop | Type | Description |
|------|------|-------------|
| experienceComponent | AccessibleExperienceComponent | Required |
| isRoot | boolean | Skips building block when true |
| Slot | Props | Description |
|------|-------|-------------|
| default | experienceComponent, isRoot | Override building-block area |
| components | components | Override nested accessible components |
DeltaAccessibleBuildingBlockComponent
Loads the building-block schema via blockLoader, resolves nested content-item dependencies with parseAccessibleExperienceComponentDependencyTree, and parses fields with parseDataFromAccessibleExperienceComponent. Needs DELTA_CONFIG (token/environment) to fetch nested items.
| Prop | Type | Description |
|------|------|-------------|
| experienceComponent | AccessibleExperienceComponent | Required |
| component | string | Registered Vue component name |
| slug | string | Building-block slug (buildingBlockSlug) |
| config | ParseDataConfig | Accepted; parsing uses the accessible parser |
| Slot | Props | Description |
|------|-------|-------------|
| default | buildingBlock, data, resolvedDependencies | Full control over render |
| loading | slug | Shown while the block schema loads |
| not-found | — | Shown when block/data unavailable |
Views
Page-level components you can mount on routes:
| Export | Role |
|--------|------|
| DeltaContentExperienceView | Fetches CDA content for the current vue-router path and renders DeltaContentExperienceComponent |
| DeltaEditorView | Listens for connector CONTENT_EXPERIENCE_SEND / ROOT_EXPERIENCE_COMPONENT_SEND |
| DeltaComponentsView | Renders every registered block with { insertPlaceholders: true } |
<script setup lang="ts">
import { DeltaEditorView } from '@lars-plate/delta-client-vue'
</script>
<template>
<DeltaEditorView />
</template>DeltaContentExperienceView always uses the GraphQL tree. For CDA v1, compose useDeltaFetchContentExperienceByPath with DeltaAccessibleContentExperienceComponent instead (as the Nuxt module does).
Component trees
GraphQL / editor:
DeltaContentExperienceComponent
└── DeltaExperienceComponentComponent (isRoot)
├── DeltaBuildingBlockComponent → your Vue component (:data, :building-block)
└── DeltaGridPlacementComponent (per placement)
└── DeltaExperienceComponentComponent (nested)
├── DeltaBuildingBlockComponent
└── …CDA / accessible:
DeltaAccessibleContentExperienceComponent
└── DeltaAccessibleExperienceComponentComponent (isRoot)
├── DeltaAccessibleBuildingBlockComponent → your Vue component (:data, :building-block)
└── DeltaAccessibleExperienceComponentComponent (nested `components`)
└── …Named exports
import {
DeltaClientVue,
DELTA_CONFIG,
DELTA_COMPONENTS,
DELTA_BLOCK_LOADER,
DELTA_CONNECTOR,
DeltaContentExperienceComponent,
DeltaExperienceComponentComponent,
DeltaBuildingBlockComponent,
DeltaGridPlacementComponent,
DeltaAccessibleContentExperienceComponent,
DeltaAccessibleExperienceComponentComponent,
DeltaAccessibleBuildingBlockComponent,
DeltaContentExperienceView,
DeltaEditorView,
DeltaComponentsView,
useDeltaClientConnector,
useDeltaComponents,
useDeltaFetchContentExperienceByPath,
} from '@lars-plate/delta-client-vue'Aliases useDelta* mirror the internal composable names (useClientConnector, useComponents, useFetchContentExperienceByPath).
Environment variables (optional)
When not providing DELTA_CONFIG:
| Variable | Purpose |
|----------|---------|
| DELTA_ENVIRONMENT | acc | dev | local | prod |
| DELTA_TOKEN | Bearer token for Content Delivery API |
| DELTA_VERSION | API version segment (v0 or v1) |
With Vite, expose them on import.meta.env (for example DELTA_ENVIRONMENT in .env).
Typical integration patterns
| Pattern | Approach |
|---------|----------|
| Public site / CDA v0 | useDeltaFetchContentExperienceByPath + DeltaContentExperienceComponent |
| Public site / CDA v1 | Same fetch + DeltaAccessibleContentExperienceComponent |
| Editor iframe | DeltaEditorView or useDeltaClientConnector + CONTENT_EXPERIENCE_SEND / ROOT_EXPERIENCE_COMPONENT_SEND |
| Design system preview | DeltaComponentsView or useDeltaComponents + DeltaBuildingBlockComponent + blockLoader |
| Fully custom UI | Default slots on each Delta* component |
| Headless data only | @lars-plate/delta-client parsers; use these components only where helpful |
Development
# Install dependencies (monorepo root)
pnpm install
# Build this library (vue-tsc types via Vite + dts)
pnpm nx build delta-client-vue
# Run unit tests
pnpm nx test delta-client-vueOutput: dist/index.js (ESM), dist/index.d.ts, and extracted CSS at dist/index.css.
License
MIT © Lars Baalmans
