Skip to content

drunomics/nuxt-component-preview

Repository files navigation

Nuxt Component Preview

npm version npm downloads ci License Nuxt

A Nuxt module by drunomics for previewing Vue components in external contexts (like iframes or separate HTML pages). Originally developed for use with decoupled Drupal environments, but can be used with any backend.

Features

  • 🎭 Component Preview Mode: Conditionally render components for previewing in isolation
  • 🚀 Production Safe: Inactive by default, only activates when explicitly enabled
  • 🎯 Target Rendering: Render components to specific DOM elements using CSS selectors
  • đź“‹ Component Index: Auto-generates JSON metadata for global components (Drupal Canvas compatible)
  • đź§Ş Testing Ready: Comprehensive test coverage and playground setup

Quick Setup

Install the module to your Nuxt application:

npm install nuxt-component-preview

Add it to your nuxt.config.ts:

export default defineNuxtConfig({
  modules: [
    'nuxt-component-preview',
  ]
})

Cross-domain configuration

When embedding component previews on a different domain, disable the app manifest in development mode only:

export default defineNuxtConfig({
  modules: [
    'nuxt-component-preview',
  ],

  // Disable appManifest in development only
  $development: {
    experimental: {
      appManifest: false
    }
  }
})

Why? In dev mode, Nuxt's appManifest feature (default since v3.8) tries to fetch metadata using relative URLs that fail in cross-domain contexts. Production builds work fine with appManifest enabled. Since component preview doesn't require this feature, it's safe to disable in development.

CORS Configuration

For cross-origin embedding, configure CORS in your Nuxt app:

export default defineNuxtConfig({
  // Development: Vite server CORS
  vite: {
    server: {
      cors: {
        origin: ['https://your-backend.com'],
      },
    },
  },

  // Production: Nitro route rules
  nitro: {
    routeRules: {
      '/nuxt-component-preview/*.js': {
        cors: true,
        headers: {
          'Access-Control-Allow-Origin': 'https://your-backend.com',
        },
      },
      '/_nuxt/**': {
        cors: true,
        headers: {
          'Access-Control-Allow-Origin': 'https://your-backend.com',
        },
      },
    },
  },
})

Usage

Rendering Component Previews

To render a component preview, use the <ComponentPreviewArea /> component in your app.

Example app.vue

<template>
  <ComponentPreviewArea v-if="useRuntimeConfig().public.componentPreview" />
  <NuxtPage v-else />
</template>

Using the App Loader

The app-loader script automatically sets up everything needed for component preview:

<!DOCTYPE html>
<html>
<head>
  <title>Component Preview</title>
  <script src="http://localhost:3000/nuxt-component-preview/app-loader.js"></script>
</head>
<body>
  <h1>Component Preview Page</h1>

  <!-- Preview targets where components will render -->
  <div id="preview-target-1"></div>
  <div id="preview-target-2"></div>

  <script>
    // Helper function that handles both sync and async cases:
    const onNuxtComponentPreviewReady = (callback) =>
      window.__nuxtComponentPreviewApp
        ? callback(window.__nuxtComponentPreviewApp)
        : window.addEventListener('nuxt-component-preview:ready', event => callback(event.detail.nuxtApp), { once: true })

    // Example usage of the helper:
    onNuxtComponentPreviewReady((nuxtApp) => {
      nuxtApp.$previewComponent('MyComponent', { prop: 'value' }, {}, '#preview-target-1');
      nuxtApp.$previewComponent('OtherComponent', { data: 123 }, {}, '#preview-target-2');
      nuxtApp.$previewComponent('LayoutComponent', {}, { slot_name: '<p>HTML content for slot</p>' }, '#preview-target-3');
    });
  </script>
</body>
</html>

See playground/public/preview-test-loader.html for a working example.

API Reference

nuxt-component-preview:ready Event

Fired when the Nuxt app is ready for component preview:

window.addEventListener('nuxt-component-preview:ready', (event) => {
  const { nuxtApp } = event.detail;
  // Use nuxtApp.$previewComponent() here
});

Helper Function Pattern

For convenience, you can use this helper that works whether Nuxt is already ready or still loading:

const onNuxtComponentPreviewReady = (callback) =>
  window.__nuxtComponentPreviewApp
    ? callback(window.__nuxtComponentPreviewApp)
    : window.addEventListener('nuxt-component-preview:ready', event => callback(event.detail.nuxtApp), { once: true })

// Usage
onNuxtComponentPreviewReady((nuxtApp) => {
  nuxtApp.$previewComponent('MyComponent', props, slots, '#target');
});

$previewComponent(componentName, props, slots, targetSelector)

Renders a Vue component to a target element. Returns a Promise that resolves when rendering completes.

Parameters (in order):

  • componentName (string): Name of the registered Vue component
  • props (object, optional): Props to pass to the component (default: {})
  • slots (object, optional): Slot content as HTML strings, keyed by slot name (default: {})
  • targetSelector (string | Element): CSS selector or DOM element where component will be rendered

Returns: Promise<{unmount: Function}>

// Simple component
await nuxtApp.$previewComponent('TestCard', { title: 'My Card' }, {}, '#preview-target');

// Component with slots
await nuxtApp.$previewComponent(
  'TwoColumnLayout',
  { width: 33 },
  {
    'column-one': '<h3>First Column</h3><p>Content</p>',
    'column-two': '<h3>Second Column</h3><p>Content</p>'
  },
  '#preview-target'
);

Nested Components: Slots can contain additional preview containers. An example implementing rendering with an arbitrary depth can be found at the example, which can be tested via npm run dev.

Component Index

This module automatically generates a component index JSON file containing metadata for all global components. This is particularly useful for integration with Drupal Canvas External JS module.

Endpoint

The component index is available at:

http://localhost:3000/nuxt-component-preview/component-index.json

Configuration

export default defineNuxtConfig({
  modules: ['nuxt-component-preview'],

  componentPreview: {
    componentIndex: {
      enabled: true, // default: true
      category: 'Nuxt Components', // default category
      status: 'stable', // default: stable, experimental, deprecated, obsolete

      // Exclude components (overwrites defaults)
      exclude: {
        components: ['*--default'], // default: excludes *--default pattern
        directories: [] // exclude by directory pattern
      },

      // Override metadata for specific components
      overrides: {
        TestButton: { category: 'Forms', status: 'experimental' }
      }
    }
  }
})

Requirements for Component Index

  • Components must be global (registered with global: true in Nuxt)
    • Components in components/global/ directory are automatically global
    • Nuxt modules can also register global components
  • Use TypeScript inline syntax with JSDoc for best metadata extraction:
<script setup lang="ts">
withDefaults(defineProps<{
  /**
   * Button label text
   * @example Submit
   * @example Cancel
   */
  label?: string
  /**
   * Button variant
   * @example primary
   * @enumLabels {"large": "Extra Large (XL)"}
   */
  variant?: 'primary' | 'secondary' | 'large'
}>(), {
  label: 'Click me',
  variant: 'primary'
})
</script>

Supported JSDoc tags:

  • @example - Adds to examples field
  • @enumLabels - Custom labels for meta:enum (full or partial)

Testing

This module includes comprehensive tests. To run them:

npm run test

Releasing

Run command

npm run release -- --release-as 1.0.0-alpha.1

About

This module is maintained by drunomics and inspired by the needs of decoupled Drupal projects, such as nuxtjs-drupal-ce.