@ethereal-ui/vite-plugin-react-preview

3.1.0 • Public • Published

@ethereal-ui/vite-plugin-react-preview

NPM Version

Quickly preview React components with Vite.

  • 🍃 Lightweight: Thin layer on top of Vite functionality.
  • 🛠️ Minimal configuration: Add one plugin, and you’re ready.
  • ⚡️ Hot-reload: Instant preview updates.
  • 🚀 Non-intrusive: Doesn’t interfere with your production bundle.
  • 🔌 Extensible: Wrap previews with your React components for theming or visual helpers.
  • 🤝 Compatible with StoryBook
  • No Telemetry

Installation

# npm
npm install @ethereal-ui/vite-plugin-react-preview

# yarn
yarn add @ethereal-ui/vite-plugin-react-preview

# pnpm
pnpm add @ethereal-ui/vite-plugin-react-preview

Usage

Once you install the plugin, add it to your Vite configuration:

import { defineConfig } from 'vite';
import reactPreview from '@ethereal-ui/vite-plugin-react-preview';

export default defineConfig({
  // ...
  plugins: [reactPreview()],
});

Create a file with the extension .preview.tsx (or .preview.jsx) in your source directory. The plugin will treat each export containing a valid React component type as a view to visualize.

Run Vite and open a browser using the route /_preview. Your preview file will be listed there; click it to view it. The preview uses Vite’s hot reload, keeping it up-to-date as you change the code.

[!TIP] Combine this plugin with VSCode built-in Simple Browser -or a similar extension- to get a side-by-side instant preview of your React component.

Usage with Storybook

In most cases, the preview files are compatible with the Storybook CSFv2 format, so you can include them as another story. The plugin will ignore any exports that are not React component types, allowing you to use CSFv3 objects side-by-side with the exported previews.

Configuration

Files to Include

The plugin will look for files matching **/*.preview.{jsx,tsx} by default, but you can change that with the include option:

export default defineConfig({
  plugins: [
    reactPreview({
      include: '**/*.stories.tsx',
    }),
  ],
});

[!NOTE] While it’s possible to include any file exporting a valid React component, using **/*.tsx is not recommended. A project may contain many React components; not all will render correctly without the proper setup.

If you like to embed the preview alongside your component code, see the Previews in the same component file section.

The include option supports string arrays and excludes patterns (the plugin uses Vite glob imports):

export default defineConfig({
  plugins: [
    reactPreview({
      include: ['./dir/*.js', '!**/bar.js'],
    }),
  ],
});

Preview Server Route

The plugin hooks into the dev server’s /_preview route, but you can change that with the route option:

export default defineConfig({
  plugins: [
    reactPreview({
      route: '/other', // respond to http://localhost:<port>/other
    }),
  ],
});

Advanced Configuration

While this plugin provides minimal functionality for previewing React components, you can extend it and customize how components are obtained and rendered from a module.

The plugin supports an option called viewResolverFactory. This option receives an object that imitates the JavaScript import statement. The from field indicates the module to import, and the import field indicates the name to import:

export default defineConfig({
  plugins: [
    reactPreview({
      viewResolverFactory: {
        import: 'myViewResolverFactory',
        from: './src/myViewResolverFactory',
      },
    }),
  ],
});

What is a viewResolverFactory?

A viewResolverFactory is a function that receives the loaded preview module and returns an object implementing the ViewResolver interface. To parse the previous sentence, we'll need to explain some of the terms used by the plugin implementation.

A preview module exports views. A View is an object with a name, title, and the React component to render.

type View = Readonly<{
  name: string;
  title: string;
  component: ComponentType<ViewComponentProps>;
}>;

A ViewResolver translates a module (an unknown object loaded by a dynamic import) into instances of View:

type ViewResolver = {
  readonly views: readonly View[];
  findView(viewName?: string): View | undefined;
};

A ViewResolverFactory is a function that receives the loaded module and returns a ViewResolver:

type ViewResolverFactory = {
  (loadedModule: unknown): ViewResolver;
};

Putting all together

If you like to wrap the views with your component, you can create a custom ViewResolverFactory that uses the default factory included in the plugin:

import {
  defaultViewResolverFactory,
  type ViewResolverFactory,
  type ViewWrapperProps,
} from '@ethereal-ui/vite-plugin-react-preview/viewer';

const MyViewWrapper = ({ viewTitle, children }: ViewWrapperProps) => (
  <div>
    <h1>{viewTitle}</h1>
    <div>{children}</div>
  </div>
);

export const myViewResolverFactory: ViewResolverFactory = loadedModule =>
  defaultViewResolverFactory(loadedModule, { viewWrapper: MyViewWrapper });

Then, set up that ViewResolverFactory in the plugin options:

export default defineConfig({
  plugins: [
    reactPreview({
      viewResolverFactory: {
        import: 'myViewResolverFactory',
        from: './src/myViewResolverFactory',
      },
    }),
  ],
});

A complex ViewResolverFactory implementation can take the loaded module and create a React component based on its exported value (like StoryBook's CSFv3 does):

import type { ViewResolverFactory } from '@ethereal-ui/vite-plugin-react-preview/viewer';

export const myViewResolverFactory: ViewResolverFactory = loadedModule => {
  // This is an example; these functions don't exist in the plugin
  const views = transformCSFObjectsIntoViews(loadedModule);
  const findView = createFindViewImpl(views);

  return {
    views,
    findView,
  };
};

Previews in the same component file

Writing the preview alongside your component code might be handy during development:

export const MyComponent = props => <MyComponentContents />;

export const preview = () => {
  const props = setUpMyComponentPreview();
  return <MyComponent {...props} />;
};

[!NOTE] The main reason why this is not the plugin default is that while tree-shaking can ignore unused exports, the setup code may refer to problematic dependencies that break your modularization layers or cause cycles.

However, this may be useful for a quick-and-dirty component playground.

To have previews alongside your component code, change the default include plugin option to include every React component. Also, add a custom view resolver factory that only includes the preview export.

Plugin Configuration (vite.config.ts)

import { defineConfig } from 'vitest/config';
import reactPreview from '@ethereal-ui/vite-plugin-react-preview';

export default defineConfig({
  plugins: [
    reactPreview({
      include: '**/*.tsx',
      viewResolverFactory: {
        import: 'viewResolverFactory',
        from: './src/viewResolverFactory',
      },
    }),
  ],
});

Custom view resolver factory (./src/viewResolverFactory.ts):

import {
  defaultViewResolverFactory,
  type ViewResolverFactory,
} from '@ethereal-ui/vite-plugin-react-preview/viewer';

export const viewResolverFactory: ViewResolverFactory = loadedModule =>
  defaultViewResolverFactory(loadedModule, {
    viewFilter: name => name === 'preview',
  });

/@ethereal-ui/vite-plugin-react-preview/

    Package Sidebar

    Install

    npm i @ethereal-ui/vite-plugin-react-preview

    Weekly Downloads

    1

    Version

    3.1.0

    License

    MIT

    Unpacked Size

    50.8 kB

    Total Files

    11

    Last publish

    Collaborators

    • dfernandez79