Astrobook

repository·master·Indexed 42 days ago

https://github.com/ocavue/astrobook

A minimal UI component playground for the Astro ecosystem. It allows developers to develop, test, and showcase components from multiple frameworks—including React, Vue, Svelte, Preact, Solid, Lit, and Astro—in a unified environment using a Storybook-like syntax with support for args and decorators.

Tokens
10.5K
Snippets
35
Records
46
Agent score
32%

What's inside Astrobook

  1. Use single-story hoisting

    master

    If a story's named export matches the module's filename (e.g., Button.stories.ts exporting const Button = {}), Astrobook will automatically hoist that story to replace the parent component in the UI. This prevents redundant nesting in the sidebar (e.g., showing Button instead of Button/Button).

    // Button.stories.ts
    import { Button as ButtonComponent } from './Button.tsx'
    
    export default {
      component: ButtonComponent,
    }
    
    // Single named export matching module name
    export const Button = {}
  2. Use decorators in stories

    master

    Decorators are objects used to wrap a component with additional logic or styling. A decorator must have a component (the wrapper) and can optionally take props. The wrapper component must include a slot to render the children.

    Note: Decorators are rendered into HTML by Astro and sent to the client. They currently support styling changes but cannot change component context or client-side behaviors.

    // Button.stories.tsx
    import RedBorderDecorator from './AstroRedBorderDecorator.astro'
    import { Button, type ButtonProps } from './Button.tsx'
    import { GreenBorderDecorator } from './ReactGreenBorderDecorator.tsx'
    
    export default {
      component: Button,
    }
    
    export const PrimaryButton = {
      args: { variant: 'primary' } satisfies ButtonProps,
      decorators: [
        { component: GreenBorderDecorator, props: { width: 10 } },
        { component: RedBorderDecorator },
      ],
    }
  3. Enable Astrobook only in development

    master

    To prevent Astrobook from being included in your production build, use process.env.NODE_ENV to conditionally add the integration in your astro.config.mjs. This is ideal for adding Astrobook to an existing Astro project without affecting the production site.

    // astro.config.mjs
    import { defineConfig } from 'astro/config'
    import astrobook from 'astrobook'
    
    // https://astro.build/config
    export default defineConfig({
      integrations: [
        // On development, Astrobook is available at http://localhost:4321/astrobook.
        // On production, Astrobook is not included.
        process.env.NODE_ENV === 'development'
          ? astrobook({ subpath: '/astrobook' })
          : null,
      ],
    })
  4. Write component stories

    master

    Astrobook scans for files matching *.stories.{ts,tsx,js,jsx,mts,mjs}. It supports a subset of Storybook's Component Story Format v3, specifically args and decorators. Each file must have a default export (the component metadata) and one or more named exports (the stories).

    // src/components/Button.stories.ts
    import { Button, type ButtonProps } from './Button.tsx'
    
    export default {
      component: Button,
    }
    
    export const PrimaryButton = {
      args: { variant: 'primary' } satisfies ButtonProps,
    }
    
    export const SecondaryButton = {
      args: { variant: 'secondary' } satisfies ButtonProps,
    }
  5. Use a custom home component with Astrobook

    master

    To replace the default Astrobook landing page with your own custom component, you can provide a custom component to the Astrobook configuration. This allows you to design a unique entry point for your component library documentation.

    /* Note: This example demonstrates the pattern of providing a custom component for the home page in the Astrobook configuration. */
  6. Quick start with Astrobook

    master

    Astrobook is a minimal UI component playground for frameworks like React, Vue, Preact, Svelte, Solid, Lit, and Astro. To set it up in a new project:

    1. Install dependencies:
      npm install astro @astrojs/react astrobook
    2. Configure Astro: Add astrobook to your integrations array in astro.config.mjs.
    3. Add scripts to package.json (dev, build, preview).
    4. Update .gitignore to exclude dist/ and .astro/.
    5. Write stories in .stories.{ts,tsx,js,jsx,mts,mjs} files using a default export for the component and named exports for individual stories.
    6. Run the dev server:
      npm run dev
      View your stories at http://localhost:4321.
    npm install astro @astrojs/react astrobook
  7. Add Astrobook to an existing Astro project

    master
    The example-mixed project demonstrates how to integrate Astrobook into an existing Astro codebase rather than starting from a fresh Astrobook template. This is useful when you want to use Astrobook's component documentation capabilities alongside your current Astro application structure.
  8. How single-story hoisting works

    master

    Astrobook optimizes the navigation tree by hoisting stories when a module acts as a simple wrapper for a single story.

    Using the isSingleStory logic, a module is considered a candidate for hoisting if:

    1. It contains exactly one story (module.stories.length === 1).
    2. The name of that single story matches the name of the module (module.stories[0].name === module.name).

    When this condition is met, the StoryNode is added directly to the parent directory's children array, skipping the creation of a ModuleNode level. This results in a flatter, cleaner navigation structure.

    export function isSingleStory(module: StoryModule): boolean {
      return module.stories.length === 1 && module.stories[0].name === module.name
    }
  9. Customize the built-in home page with `homeContent`

    master

    If you are not using a custom home component, you can use the homeContent option to modify the default dashboard. You can set fields to a custom value, set them to false to hide them, or omit them to use defaults.

    FieldTypeDefaultDescription
    titlestring | false'Astrobook'Main heading on the home page.
    subtitlestring | false'The minimal UI component playground'Text below the title.
    versionobject | false{}Version badge configuration.
    version.hrefstringAstrobook CHANGELOG URLLink target for the badge.
    version.labelstringCurrent Astrobook versionText displayed in the badge.
    repoobject | false{}Repository badge configuration.
    repo.hrefstring'https://github.com/ocavue/astrobook'Link target for the badge.
    repo.labelstring'Star on GitHub'Text displayed in the badge.
    import { version } from './package.json'
    
    export default defineConfig({
      integrations: [
        astrobook({
          homeContent: {
            title: 'Acme UI',
            subtitle: 'Internal component library',
            version: {
              label: `v${version}`,
              href: 'https://example.com/CHANGELOG',
            },
            repo: {
              href: 'https://github.com/acme/ui',
              label: 'View on GitHub',
            },
          },
        }),
      ],
    })
  10. Sidebar search state and persistence

    master

    The sidebar search uses sessionStorage to persist the search state across page navigations. This ensures that if a user searches for something and clicks a link, the search panel remains open and the query is preserved.

    Storage Keys:

    • astrobook-sidebar-search-open: Set to '1' if the search panel is open.
    • astrobook-sidebar-search-query: Stores the current search string.

    Search Logic: Search is implemented using CSS filtering. The script injects a <style> element with the ID astrobook-search-style that contains rules to hide tree items ([data-search-text]) that do not match the search terms.

  11. Configure Astrobook options

    master

    You can customize Astrobook via the astrobook() integration in astro.config.mjs.

    directory

    Specify the directory to scan for stories. Defaults to the current working directory.

    astrobook({ directory: 'src/components' })

    subpath

    Specify the subpath where Astrobook is hosted. This is relative to your Astro project's base path.

    astrobook({ subpath: '/playground' })

    css

    Provide an array of relative paths to CSS files to be imported into the Astrobook site.

    astrobook({ css: ['./src/styles/custom.css'] })

    Provide a path to an Astro component containing custom <head> elements (e.g., <link>, <style>, <script>).

    astrobook({ head: './src/components/CustomHead.astro' })

    home

    Provide a path to an Astro component to replace the default dashboard homepage.

    astrobook({ home: './src/components/CustomHome.astro' })

    title

    Set the website title.

    astrobook({ title: 'My Components Playground' })
    import { defineConfig } from 'astro/config'
    import astrobook from 'astrobook'
    
    export default defineConfig({
      integrations: [
        astrobook({
          directory: 'src/components',
          subpath: '/playground',
          css: ['./src/styles/custom.css'],
          head: './src/components/CustomHead.astro',
          home: './src/components/CustomHome.astro',
          title: 'My Components Playground',
        }),
      ],
    })