# @b10cks/vue
Vue 3 SDK for integrating [b10cks](https://www.b10cks.com), an open-source headless CMS with a composable block-based content API, into Vue applications.
## Installation
```bash
npm install @b10cks/vue @b10cks/client @b10cks/richtext
```
## Usage
### Plugin Setup
```typescript
import { createApp } from 'vue'
import { B10cksVue } from '@b10cks/vue'
import App from './App.vue'
const app = createApp(App)
app.use(B10cksVue, {
apiClientOptions: {
token: 'your-access-token',
baseUrl: 'https://api.b10cks.com/api',
},
})
app.mount('#app')
```
### Data Composables
```typescript
import { useB10cksApi } from '@b10cks/vue'
const { useContent, useContents, useBlocks } = useB10cksApi()
// Single content entry by slug
const content = useContent('home', { vid: 'published' }, { immediate: true })
// List of content entries — params accepts a typed filter object
const { data: items } = useContents(
{
language_iso: 'en',
vid: 'published',
filter: {
canonical_id: { in: [someId] },
},
},
{ immediate: Boolean(someId) }
)
const blocks = useBlocks({}, { immediate: true })
```
The `immediate` option controls whether the request fires on composable setup (`true`) or must be triggered manually (`false`). Pass `immediate: false` to defer requests that depend on reactive values not yet available.
### Components
Use `B10cksComponent` to render block-specific Vue components dynamically:
```vue
```
## Live preview & visual editing
When your app is rendered inside the b10cks visual editor, these directives and the `usePreviewContent` composable make blocks selectable and keep the preview in sync while editing. They are all no-ops outside the editor, so they are safe to leave in production output.
### Directives
```vue
{{ block.headline }}
```
### `usePreviewContent` (recommended)
`v-editable` patches a single block in place, which is enough for simple cases. For whole-tree reactive updates — including nested and rich text fields — wrap your fetched content in `usePreviewContent`. It returns a reactive ref that live-updates from the editor and is unchanged outside preview mode:
```vue
```
`usePreviewContent` accepts a plain value, a `ref`, or a getter. A plain value is captured once; a reactive source resets the preview store whenever it changes.
### Plugin options
```typescript
app.use(B10cksVue, {
apiClientOptions: { token, baseUrl },
// Offset applied when a selected block is scrolled into view, so selection
// clears a fixed app header. A number is px; strings are used verbatim.
scrollOffset: 80,
// Restrict the preview bridge handshake to known editor origins.
allowedOrigins: ['https://app.b10cks.com'],
})
```
`scrollOffset` can also be set purely in CSS — `:root { --b10cks-scroll-offset: 80px }` — with no plugin option.
## Rich Text
Rich text exports live in the `@b10cks/vue/rich-text` sub-path, so pages that do not render rich text keep it out of their entry bundle.
Use `B10cksRichText` to render a b10cks `RichTextDocument` (a TipTap/ProseMirror-style JSON document) as HTML with a dependency-free, SSR-friendly renderer.
```vue
```
You can also render rich text to an HTML string manually:
```typescript
import type { RichTextDocument } from '@b10cks/vue/rich-text'
import { renderRichText } from '@b10cks/vue/rich-text'
const document: RichTextDocument = {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'Render me to HTML',
},
],
},
],
}
const html = renderRichText(document)
```
To extract plain text (useful for search indexing or meta descriptions), use `renderRichTextAsText`:
```typescript
import type { RichTextDocument } from '@b10cks/vue/rich-text'
import { renderRichTextAsText } from '@b10cks/vue/rich-text'
const text = renderRichTextAsText(document)
// or with a custom block separator:
const inline = renderRichTextAsText(document, { blockSeparator: ' ' })
```
For repeated rendering, use the factory:
```typescript
import { createRichTextTextRenderer } from '@b10cks/vue/rich-text'
const renderer = createRichTextTextRenderer()
const text = renderer.render(document)
```
### Internal link handler
b10cks internal links carry a `url`, `title`, `anchor`, and `content` (block ID) in their attrs. Pass an `internalLinkHandler` to customise how the `href` is generated — for example to prepend a route prefix or resolve content IDs via Vue Router:
```vue
```
Or when rendering to a string:
```typescript
import { renderRichText } from '@b10cks/vue/rich-text'
const html = renderRichText(document, {
internalLinkHandler: (attrs) => `/app${attrs.url}`,
})
```
Return `null` or `undefined` from the handler to fall back to the default `url`/`href` attribute value.
### Placeholder tokens
Pass a `placeholderHandler` to resolve `{token}` placeholders embedded in rich text to real values. Return `null`/`undefined` to leave a token unresolved.
```typescript
import { renderRichText } from '@b10cks/vue/rich-text'
const html = renderRichText(document, {
placeholderHandler: (key) => ({ companyName: 'b10cks' })[key],
})
```
`B10cksRichText` also exposes `placeholderHandler` as a prop, alongside `internalLinkHandler`.
### URL safety
Link and image URLs from CMS content are validated against a scheme allowlist (default `http`/`https`/`mailto`/`tel`, plus relative URLs), so stored `javascript:` URLs cannot execute. Override the allowlist with the `allowedSchemes` prop / render option — see the [`@b10cks/richtext` URL safety docs](../richtext/README.md#url-safety).
```vue
```
> **Custom extensions are no longer used.** The renderer is a custom, dependency-free implementation — it does not run TipTap. The `extensions` prop and `createB10cksRichTextExtensions()` remain as no-op stubs for backwards compatibility only.
> **Migrating from v1:** `renderRichText` and `B10cksRichText` were previously exported from `@b10cks/vue`. Update all imports to `@b10cks/vue/rich-text`.
## License
MIT