@lars-plate/delta-client-vue
v0.0.24
Published
This is a simple utility package for the Delta Client Vue
Readme
@platecms/delta-client-vue
Vue 3 components and composables for rendering Delta content experiences: building blocks, nested experience components, grid placements, and optional iframe communication with the Delta editor.
Built on top of [@platecms/delta-client](https://www.npmjs.com/package/@platecms/delta-client) for types, data parsing, and the window connector.
Features
- Composable tree — Render a full content experience from a root
ExperienceComponent, including nested grid placements. - 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 via the Content Delivery API (
useDeltaFetchContentExperienceByPath). - Customizable rendering — Every component exposes default markup and scoped slots so you can override layout without forking the library.
Requirements
| Package | Version |
| ------------------------ | -------- |
| vue | ^3.0.0 |
| vue-router | ^5.0.6 |
| @platecms/delta-client | ^0.1.3 |
You also need **@tanstack/vue-query** configured in your app if you use useDeltaFetchContentExperienceByPath (included as a dependency of this package).
Installation
pnpm add @platecms/delta-client-vue @platecms/delta-client vue vue-router
# or
npm install @platecms/delta-client-vue @platecms/delta-client vue vue-routerQuick start
1. Register the plugin
// main.ts
import { createApp } from 'vue'
import { VueQueryPlugin } from '@tanstack/vue-query'
import { DeltaClientVue } from '@platecms/delta-client-vue'
import type { BuildingBlock } from '@platecms/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.mount('#app')blockLoader is optional but required when DeltaBuildingBlockComponent must load block definitions by slug (e.g. component preview pages) instead of receiving buildingBlock via props.
2. Provide Delta configuration
Several composables read shared config from Vue’s provide / inject under the key delta-config:
// main.ts (continued)
app.provide('delta-config', {
environment: 'dev', // 'acc' | 'dev' | 'local' | 'prod'
token: import.meta.env.VITE_DELTA_TOKEN,
version: 'v1', // Content Delivery API version segment
})| 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 | API version in the URL path |
If delta-config is not provided, composables fall back to import.meta.env.DELTA_ENVIRONMENT, DELTA_TOKEN, and DELTA_VERSION where applicable.
3. Register your building-block components
Map each building-block slug to a registered Vue component name (string passed to <component :is="...">):
// App.vue or a layout wrapper
import { useDeltaComponents } from '@platecms/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 register once at startup:
import { provide } from 'vue'
provide('components', {
'hero-block': 'HeroBlock',
'text-block': 'TextBlock',
})4. Render a content experience
<script setup lang="ts">
import type { ContentExperience } from '@platecms/delta-client/graphql'
import { DeltaContentExperienceComponent } from '@platecms/delta-client-vue'
const props = defineProps<{
contentExperience: ContentExperience
}>()
</script>
<template>
<DeltaContentExperienceComponent
: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 content experience |
| DeltaExperienceComponentComponent | Single experience component + grid children |
| DeltaBuildingBlockComponent | Resolves slug → Vue component + parsed data |
| DeltaGridPlacementComponent | Grid cell; recurses into nested experience components |
app.use(DeltaClientVue, {
blockLoader?: (slug: string) => Promise<BuildingBlock>
})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).
| Environment | Parent window origin |
| ----------- | ---------------------------------- |
| acc | https://delta-acc.getplate.rocks |
| dev | https://delta-dev.getplate.rocks |
| local | http://localhost:5173 |
| prod | https://delta.getplate.rocks |
Example — editor preview iframe
<script setup lang="ts">
import { onUnmounted, ref } from 'vue'
import {
DeltaContentExperienceComponent,
useDeltaClientConnector,
} from '@platecms/delta-client-vue'
import { ConnectorEventType } from '@platecms/delta-client/connectors'
import type { ContentExperience, ExperienceComponent } from '@platecms/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).
useDeltaComponents(initialComponents?)
Manages the slug → component name registry.
const { components, hasComponent } = useDeltaComponents({
'hero-block': 'HeroBlock',
})
if (hasComponent('hero-block')) {
// ...
}useDeltaFetchContentExperienceByPath(path)
TanStack Vue Query wrapper around the Content Delivery API.
Requires VueQueryPlugin, delta-config (environment, token, version), and a leading path such as /my-page.
| Environment | API base URL |
| ----------- | -------------------------------------- |
| acc | https://api.delta-acc.getplate.rocks |
| dev | https://api.delta-dev.getplate.rocks |
| local | http://localhost:28164 |
| prod | https://api.delta.getplate.rocks |
Request: GET {base}/content-delivery-api/{version}/content-experiences/path{path}
Header: Authorization: Bearer {token}
Example — route-driven page
<script setup lang="ts">
import { useRoute } from 'vue-router'
import {
DeltaContentExperienceComponent,
useDeltaFetchContentExperienceByPath,
} from '@platecms/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>Components
DeltaContentExperienceComponent
Top-level entry for a content experience.
| 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 when rootExperienceComponent is set.
<DeltaContentExperienceComponent
:content-experience="cx"
:root-experience-component="cx.experienceComponent"
>
<template #default="{ rootExperienceComponent }">
<!-- custom root layout -->
<DeltaExperienceComponentComponent
:experience-component="rootExperienceComponent"
is-root
/>
</template>
</DeltaContentExperienceComponent>DeltaExperienceComponentComponent
Renders one experience component: building block (if not root) + grid placements.
| Prop | Type | Description |
| --------------------- | --------------------- | -------------------------------- |
| experienceComponent | ExperienceComponent | Required |
| isRoot | boolean | Skips building block when true |
| Slot | Props | Description |
| ----------------- | ------------------------------- | --------------------------------------- |
| default | experienceComponent, isRoot | Override building-block area |
| grid-placements | gridPlacements | Override grid rendering (sorted by row) |
<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 |
Example building-block Vue component
<!-- blocks/HeroBlock.vue -->
<script setup lang="ts">
import type { BuildingBlock } from '@platecms/delta-client/graphql'
defineProps<{
buildingBlock: BuildingBlock
data: Record<string, unknown> // parsed field map
}>()
</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 '@platecms/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 (editor integration).
Component tree
DeltaContentExperienceComponent
└── DeltaExperienceComponentComponent (isRoot)
├── DeltaBuildingBlockComponent → your Vue component (:data, :building-block)
└── DeltaGridPlacementComponent (per placement)
└── DeltaExperienceComponentComponent (nested)
├── DeltaBuildingBlockComponent
└── …Named exports
Import individually (tree-shaking friendly):
import {
DeltaClientVue,
DeltaContentExperienceComponent,
DeltaExperienceComponentComponent,
DeltaBuildingBlockComponent,
DeltaGridPlacementComponent,
useDeltaClientConnector,
useDeltaComponents,
useDeltaFetchContentExperienceByPath,
} from '@platecms/delta-client-vue'Aliases useDelta* mirror the internal composable names (useClientConnector, etc.).
Environment variables (optional)
When not using app.provide('delta-config', …):
| Variable | Purpose |
| ------------------- | ------------------------------------- |
| DELTA_ENVIRONMENT | acc | dev | local | prod |
| DELTA_TOKEN | Bearer token for Content Delivery API |
| DELTA_VERSION | API version segment (e.g. v1) |
With Vite, define them in .env and expose via import.meta.env if you extend the composables’ defaults in your app.
Build & publish
This package is built as a Vue library:
pnpm install
pnpm build # vue-tsc + vite → dist/Outputs:
dist/index.js(ESM)dist/index.umd.cjs(UMD)dist/index.d.ts(types)
GraphQL codegen (for local development against a Delta schema):
pnpm codegenConfigure the schema URL in codegen.ts.
Typical integration patterns
| Pattern | Approach |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Public site / CDP | useDeltaFetchContentExperienceByPath + vue-router path |
| Editor iframe | useDeltaClientConnector + listen for CONTENT_EXPERIENCE_SEND / ROOT_EXPERIENCE_COMPONENT_SEND |
| Design system preview | useDeltaComponents + DeltaBuildingBlockComponent + blockLoader |
| Fully custom UI | Use default slots on each Delta* component |
| Headless data only | Use @platecms/delta-client parseDataFromExperienceComponent directly; use these components only where helpful |
License
MIT © Lars Baalmans
