npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bagelink/blox

v1.12.53

Published

Blox page builder library for drag-and-drop page building and static data management

Readme

@bagelink/blox

Blox is a Vue 3 library for building drag-and-drop page builders and managing static data for web applications. It provides a complete solution for creating external preview systems and rendering dynamic page content.

Features

  • 🎨 Page Builder Integration - Full support for drag-and-drop page building
  • 📄 Static Page Management - Manage JSON-based static page data
  • 🔌 Component Registry - Flexible component registration system
  • 📡 Communication System - Built-in messaging for builder-preview communication
  • 🎯 Type-Safe - Full TypeScript support
  • 🖼️ Base Components - Pre-built components (Button, Text, Image, etc.)
  • 📱 Responsive - Mobile and desktop support

Installation

npm install @bagelink/blox
# or
pnpm add @bagelink/blox
# or
yarn add @bagelink/blox

@bagelink/blox

Blox is a Vue 3 library for building drag-and-drop page builders and managing static data for web applications. It provides a complete solution for creating external preview systems and rendering dynamic page content.

Features

  • 🎨 Page Builder Integration - Full support for drag-and-drop page building
  • 📄 Static Page Management - Manage JSON-based static page data
  • 🔌 Component Registry - Flexible component registration system
  • 📡 Communication System - Built-in messaging for builder-preview communication
  • 🎯 Type-Safe - Full TypeScript support
  • 🖼️ Base Components - Pre-built components (Button, Text, Image, etc.)
  • 📱 Responsive - Mobile and desktop support

Installation

npm install @bagelink/blox vue-router
# or
pnpm add @bagelink/blox vue-router
# or
yarn add @bagelink/blox vue-router

Quick Start (Recommended)

The simplest way to set up Blox in your Vue application:

// main.ts
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { createBlox, ButtonConfig, TextConfig, TitleConfig } from '@bagelink/blox'
import '@bagelink/blox/dist/blox.css'

import App from './App.vue'

const app = createApp(App)
const router = createRouter({
	history: createWebHistory(),
	routes: [
		// Your app routes
	],
})

// Create and configure Blox
const blox = createBlox()
blox.registerComponents([ButtonConfig, TextConfig, TitleConfig]).registerRoutes(router)

app.use(blox)
app.use(router)
app.mount('#app')

That's it! Preview routes are now available at:

  • /blox/preview/:pageId? - External preview with editor communication
  • /blox/render/:pageId? - Static page rendering

With Custom Components

import { createBlox, createComponentConfig, ButtonConfig } from '@bagelink/blox'
import MyHeroSection from './components/MyHeroSection.vue'

const MyHeroConfig = createComponentConfig({
	id: 'MyHero',
	label: 'Hero Section',
	icon: 'hero_section',
	component: MyHeroSection,
	content: [
		{
			type: 'text',
			key: 'title',
			label: 'Hero Title',
			defaultValue: 'Welcome!',
		},
	],
})

const blox = createBlox()
blox.registerComponents([ButtonConfig, MyHeroConfig]).registerRoutes(router)

app.use(blox)

Available Built-in Component Configs

Import these configs to register built-in components:

  • ButtonConfig - Customizable button/link
  • TextConfig - Rich text content
  • TitleConfig - Heading with optional subtitle
  • ImageConfig - Responsive image
  • SpacerConfig - Vertical spacing
  • ContainerConfig - Content container with max-width

For more examples, see SETUP_EXAMPLE.md.


Alternative Setup (Legacy)

1. Register Base Components

import { createApp } from 'vue'
import { registerBaseComponents } from '@bagelink/blox'

const app = createApp(App)

// Register all base components
registerBaseComponents(app)

app.mount('#app')

2. Use the RenderPage Component

<template>
	<RenderPage :page-data="pageData" />
</template>

<script setup lang="ts">
import { RenderPage } from '@bagelink/blox'

const pageData = {
  components: [...],
  pageSettings: {...}
}
</script>

3. Use ExternalPreview for Page Builder

<template>
	<ExternalPreview :origin="editorOrigin" />
</template>

<script setup lang="ts">
import { ExternalPreview } from '@bagelink/blox'

const editorOrigin = 'https://your-editor.com'
</script>

Core Concepts

Component Registry

Register your custom components to make them available in the page builder:

import { registerComponent, registerComponents } from '@bagelink/blox'
import MyButton from './MyButton.vue'

// Register a single component
registerComponent('Button', MyButton)

// Or register multiple components at once
registerComponents({
	Button: MyButton,
	Hero: MyHero,
	Text: MyText,
})

Communication System

Blox includes a built-in communication system for coordinating between the builder and preview:

import { createCommunicationBridge } from '@bagelink/blox'

// Create a communication bridge
const bridge = createCommunicationBridge({
	origin: 'https://editor.com',
	targetWindow: window.parent,
})

// Listen for messages
bridge.on('update', ({ data }) => {
	console.log('Received update:', data)
})

// Send messages
bridge.send('ready', { components: ['Button', 'Hero'] })

Styling Utilities

Use the built-in styling utilities to apply consistent styles:

import { generateBlockStyles, getResponsiveClasses } from '@bagelink/blox'

const styles = generateBlockStyles(blockData, false)
const classes = getResponsiveClasses(blockData)

Base Components

The library includes these pre-built components:

  • Button - Customizable button with variants
  • Container - Layout container with spacing
  • Image - Responsive image component
  • Spacer - Vertical/horizontal spacing
  • Text - Rich text content
  • Title - Heading component

API Reference

Registry API

registerComponent(type: string, component: ComponentConstructor)

Register a single component.

import { registerComponent } from '@bagelink/blox'
import MyButton from './MyButton.vue'

registerComponent('Button', MyButton)

Parameters:

  • type - The block type identifier (e.g., 'Button', 'Hero')
  • component - Vue component constructor or async import

registerComponents(components: ComponentRegistry)

Register multiple components at once.

import { registerComponents } from '@bagelink/blox'

registerComponents({
	Button: MyButton,
	Hero: MyHero,
	Text: MyText,
})

Parameters:

  • components - Object mapping type names to components

getComponent(type: string): ComponentConstructor | undefined

Get a registered component by type.

const ButtonComponent = getComponent('Button')

hasComponent(type: string): boolean

Check if a component type is registered.

if (hasComponent('Button')) {
	// Button is registered
}

getAllComponents(): ComponentRegistry

Get all registered components.

const allComponents = getAllComponents()

unregisterComponent(type: string)

Remove a component from the registry.

unregisterComponent('Button')

clearRegistry()

Remove all registered components.

clearRegistry()

getRegisteredTypes(): string[]

Get array of all registered type names.

const types = getRegisteredTypes()
// ['Button', 'Hero', 'Text']

createNamespacedRegistry(namespace: string)

Create a namespaced registry for multi-tenant scenarios.

const siteA = createNamespacedRegistry('site-a')
siteA.register('Button', ButtonA)

const siteB = createNamespacedRegistry('site-b')
siteB.register('Button', ButtonB)

Returns: Object with methods:

  • register(type, component) - Register to namespace
  • get(type) - Get from namespace
  • getAll() - Get all from namespace

Communication API

createCommunicationBridge(config: CommunicationConfig): CommunicationBridge

Create a communication bridge for postMessage communication.

import { createCommunicationBridge } from '@bagelink/blox'

const bridge = createCommunicationBridge({
	origin: 'https://editor.com',
	targetWindow: window.parent,
})

Parameters:

  • config.origin - Allowed origin (string or '*')
  • config.targetWindow - Target window (default: window.parent)
  • config.onMessage - Optional global message handler

CommunicationBridge.on(type: MessageType, handler: MessageHandler): () => void

Register a message handler.

const unsubscribe = bridge.on('update', ({ data }) => {
	console.log('Received update:', data)
})

// Later: unsubscribe()

Parameters:

  • type - Message type or '*' for all messages
  • handler - Handler function

Returns: Unsubscribe function


CommunicationBridge.off(type: MessageType, handler: MessageHandler)

Unregister a message handler.

bridge.off('update', myHandler)

CommunicationBridge.send(type: MessageType, message?: any, data?: any)

Send a message to the target window.

bridge.send('focus', 'block-123')
bridge.send('update', null, { components: [...] })

Parameters:

  • type - Message type
  • message - Optional message payload
  • data - Optional data payload

CommunicationBridge.destroy()

Clean up and remove all listeners.

bridge.destroy()

sendMessage(type, message?, targetWindow?, origin?)

Send a one-off message without creating a bridge.

import { sendMessage } from '@bagelink/blox'

sendMessage('ready', { components: ['Button', 'Hero'] })

Renderer API

initializePage(pageData: PageData): Promise<void>

Initialize a page with all settings and assets.

import { initializePage } from '@bagelink/blox'

await initializePage(pageData)

What it does:

  • Injects responsive CSS
  • Applies page settings
  • Loads Google Fonts
  • Injects custom code

injectResponsiveCSS()

Inject the responsive CSS system.

import { injectResponsiveCSS } from '@bagelink/blox'

injectResponsiveCSS()

injectCode(code: string | undefined, target: 'head' | 'body')

Inject custom code into head or body.

injectCode('<script>console.log("Hello")</script>', 'head')

loadGoogleFont(fontName: string): Promise<void>

Load a Google Font.

await loadGoogleFont('Roboto')

loadComponentFonts(components: ComponentData[]): Promise<void>

Load all fonts used in components.

await loadComponentFonts(pageData.components)

applyGlobalFont(fontName: string)

Apply a font globally to the page.

applyGlobalFont('Inter')

applyPageSettings(pageData: PageData)

Apply all page settings to the document.

applyPageSettings(pageData)

Styles API

generateBlockStyles(data: Record<string, any>, isMobile?: boolean): Record<string, string>

Generate CSS styles from block data.

import { generateBlockStyles } from '@bagelink/blox'

const styles = generateBlockStyles(blockData, false)
// { 'margin-top': '2rem', 'padding': '1rem', ... }

Parameters:

  • data - Block data object
  • isMobile - Whether to use mobile overrides (default: false)

Returns: CSS styles object


getResponsiveClasses(data: Record<string, any>): string[]

Get responsive CSS classes for a block.

const classes = getResponsiveClasses(blockData)
// ['blox-block', 'responsive-colors', 'full-width-mobile']

getResponsiveCSS(): string

Get the responsive CSS to inject.

const css = getResponsiveCSS()

Normalizer API

normalizeComponentData(data: Record<string, any>): Record<string, any>

Normalize component data (convert string booleans, numbers, etc.).

import { normalizeComponentData } from '@bagelink/blox'

const normalized = normalizeComponentData({
	width: '800',
	fullWidth: 'true',
	title: 'Hello',
})
// { width: 800, fullWidth: true, title: 'Hello' }

deepClone<T>(obj: T): T

Deep clone an object.

const cloned = deepClone(originalData)

deepMerge(target: any, source: any): any

Deep merge two objects.

const merged = deepMerge(defaults, customSettings)

Components

ExternalPreview

Preview component for external sites.

<ExternalPreview :origin="editorOrigin" :initial-page-data="pageData" />

Props:

  • origin?: string - Allowed editor origin (default: '*')
  • initialPageData?: PageData - Optional initial page data

Events: Communicates via postMessage with these message types:

  • ready - Sent when preview is ready
  • focus - Sent when block is focused
  • highlight - Sent when block is highlighted

RenderPage

Production render component.

<RenderPage :page-data="pageData" :mobile-breakpoint="910" />

Props:

  • pageData: PageData - Required page data to render
  • mobileBreakpoint?: number - Window width for mobile (default: 910)

TypeScript Types

ComponentData

interface ComponentData {
	id: string
	type: string
	data: Record<string, any>
}

PageData

interface PageData {
	id?: string
	components: ComponentData[]
	pageSettings?: PageSettings
	header_code?: string
	body_code?: string
	language?: Record<string, any>
}

PageSettings

interface PageSettings {
	selectedGoogleFont?: string
	pageLanguage?: string
	pageDirection?: 'ltr' | 'rtl'
	additionalHeadCode?: string
	additionalBodyCode?: string
	customFavicon?: string
	customOgImage?: string
	customKeywords?: string
	customRobotsMeta?: string
	customAnalyticsCode?: string
	disableGlobalHeadCode?: boolean
	disableGlobalBodyCode?: boolean
	disableGoogleAnalytics?: boolean
	disableFacebookPixel?: boolean
}

GlobalSettings

interface GlobalSettings {
	selectedGoogleFont?: string
	pageLanguage?: string
	pageDirection?: 'ltr' | 'rtl'
	globalHeadCode?: string
	globalBodyCode?: string
	favicon?: string
	ogImage?: string
	siteKeywords?: string
	googleAnalyticsId?: string
	facebookPixelId?: string
}

MessageType

type MessageType =
	| 'ready'
	| 'update'
	| 'focus'
	| 'highlight'
	| 'preview'
	| 'meta'
	| 'delete'
	| 'disableLinks'
	| 'newBlock'

EditorMessage

interface EditorMessage {
	type: MessageType
	message?: any
	data?: any
	isMobile?: boolean
}

BlockProps

interface BlockProps {
	isMobile?: boolean
	[key: string]: any
}

StyleConfig

interface StyleConfig {
	// Spacing
	marginTop?: number
	marginBottom?: number
	marginTopMobile?: number
	marginBottomMobile?: number
	padding?: number
	paddingMobile?: number

	// Width
	width?: number
	widthMobile?: number
	widthPercent?: number
	widthPercentMobile?: number
	fullWidth?: boolean
	fullWidthMobile?: boolean

	// Colors
	backgroundColor?: string
	backgroundColorMobile?: string
	textColor?: string
	textColorMobile?: string

	// Border
	borderWidth?: number
	borderWidthMobile?: number
	borderStyle?: string
	borderStyleMobile?: string
	borderColor?: string
	borderColorMobile?: string
	borderRadius?: number
	borderRadiusMobile?: number

	// Typography
	fontFamily?: string
	fontFamilyMobile?: string
	center?: boolean
	centerMobile?: boolean

	// Effects
	shadowType?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'custom'
	shadowTypeMobile?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'custom'
	zIndex?: number
	zIndexMobile?: number

	// Visibility
	showDesktop?: boolean
	showMobile?: boolean

	// Custom
	customId?: string
	customCSS?: string
}

Message Protocol

Editor → Preview

update

Update components data.

{
  type: 'update',
  data: ComponentData[],
  isMobile: boolean
}

focus

Focus a specific block.

{
  type: 'focus',
  message: 'block-id-123'
}

highlight

Highlight a specific block.

{
  type: 'highlight',
  message: 'block-id-123'
}

preview

Toggle preview mode.

{
  type: 'preview',
  message: true | false
}

disableLinks

Disable link navigation.

{
  type: 'disableLinks',
  message: true | false
}

Preview → Editor

ready

Preview is ready.

{
  type: 'ready',
  message: {
    registeredTypes: string[]
  }
}

focus

User focused a block.

{
  type: 'focus',
  message: 'block-id-123'
}

highlight

User highlighted a block.

{
  type: 'highlight',
  message: 'block-id-123'
}

delete

User wants to delete a block.

{
  type: 'delete',
  message: 'block-id-123'
}

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

MIT © Bagel Studio

Links