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.9.156

Published

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

Downloads

5,288

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