Skip to content

GitHub Actions ​

TL;DR: Automate Storybook deployments on every push with GitHub Actions.

What You'll Learn ​

  • How the generated workflows work
  • How to customize deployment triggers
  • How to handle different environments

Prerequisites ​

  • [ ] Completed the Quick Start setup
  • [ ] Repository with GitHub Actions enabled

Example Workflows ​

The setup skill can adapt workflows to your repository. The init command generates two workflow files and commits and pushes them. The examples below show basic hosting; generated templates also include coverage and analysis steps. For searchable components, keep those steps as described under Index components for MCP.

Main Deployment (.github/workflows/deploy-storybook.yml) ​

Deploys to production on pushes to main:

yaml
name: Deploy Storybook

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build Storybook
        run: npm run build-storybook

      - name: Deploy to Scry
        env:
          STORYBOOK_DEPLOYER_API_URL: ${{ vars.SCRY_API_URL }}
          STORYBOOK_DEPLOYER_API_KEY: ${{ secrets.SCRY_API_KEY }}
          STORYBOOK_DEPLOYER_PROJECT: ${{ vars.SCRY_PROJECT_ID }}
          STORYBOOK_DEPLOYER_VERSION: latest
        run: npx @scrymore/scry-deployer --dir ./storybook-static --deploy-version latest

PR Preview (.github/workflows/deploy-pr-preview.yml) ​

Deploys previews for pull requests:

yaml
name: Deploy PR Preview

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: read
  pull-requests: write

jobs:
  deploy:
    if: github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build Storybook
        run: npm run build-storybook

      - name: Deploy Preview
        id: deploy
        env:
          STORYBOOK_DEPLOYER_API_URL: ${{ vars.SCRY_API_URL }}
          STORYBOOK_DEPLOYER_API_KEY: ${{ secrets.SCRY_API_KEY }}
          STORYBOOK_DEPLOYER_PROJECT: ${{ vars.SCRY_PROJECT_ID }}
          STORYBOOK_DEPLOYER_VERSION: pr-${{ github.event.pull_request.number }}
        run: |
          npx @scrymore/scry-deployer --dir ./storybook-static --deploy-version pr-${{ github.event.pull_request.number }}
          echo "url=${{ vars.SCRY_VIEW_URL || 'https://view.scrymore.com' }}/${{ vars.SCRY_PROJECT_ID }}/pr-${{ github.event.pull_request.number }}/" >> $GITHUB_OUTPUT

      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            const url = '${{ steps.deploy.outputs.url }}';
            const body = `## 🚀 Storybook Preview Deployed\n\n**Preview URL:** ${url}\n\n📌 **Details:**\n- **Commit:** \`${{ github.sha }}\`\n- **Branch:** \`${{ github.head_ref }}\`\n\n> This preview will be updated automatically on each commit.`;

            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });

            const botComment = comments.find(c => c.body.includes('Storybook Preview Deployed'));

            if (botComment) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: botComment.id,
                body
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body
              });
            }

Index components for MCP ​

Use --with-analysis with coverage enabled to capture and upload the metadata and screenshots needed by search. Install the Playwright browser used by the coverage runner before deploying. The deployer's generated templates include this step; ensure its browser version matches your installed runner if you customize the dependencies.

Keep these settings for PRs that need searchable previews too. A draft-PR --no-coverage condition prevents the metadata path from running. For a basic hosting-only workflow such as the examples above, indexing is not enabled.

Check both the upload result and a project-filtered MCP search. Processing is asynchronous, so components may become searchable after the Storybook is already live. The setup skill can configure and check this complete path.

Repository Variables ​

Set these in Settings → Secrets and variables → Actions → Variables:

VariableDescriptionExample
SCRY_PROJECT_IDYour project identifiermy-storybook
SCRY_API_URLUpload service endpointhttps://upload.scrymore.com
SCRY_VIEW_URLCDN viewer URLhttps://view.scrymore.com

Repository Secrets ​

Set these in Settings → Secrets and variables → Actions → Secrets:

SecretDescription
SCRY_API_KEYYour project API key

Security

Never commit API keys to your repository. Always use GitHub Secrets.

Customizing Workflows ​

Different Build Commands ​

If your build command differs from build-storybook:

yaml
- name: Build Storybook
  run: npm run storybook:build  # Your custom command

Additional Build Steps ​

Add steps before deployment:

yaml
- name: Run Tests
  run: npm test

- name: Lint
  run: npm run lint

- name: Build Storybook
  run: npm run build-storybook

Deploy on Tags ​

Deploy on release tags:

yaml
on:
  push:
    tags:
      - 'v*'

# In deploy step:
STORYBOOK_DEPLOYER_VERSION: ${{ github.ref_name }}

Matrix Builds ​

Deploy multiple Storybooks:

yaml
jobs:
  deploy:
    strategy:
      matrix:
        storybook: [main, components, patterns]
    steps:
      # ...
      - name: Deploy
        env:
          STORYBOOK_DEPLOYER_PROJECT: my-project-${{ matrix.storybook }}
        run: npx @scrymore/scry-deployer --dir ./storybook-${{ matrix.storybook }}

Conditional Deployment ​

Deploy only when Storybook files change:

yaml
on:
  push:
    branches: [main]
    paths:
      - 'src/**/*.stories.*'
      - '.storybook/**'
      - 'package.json'

Environment Variables Reference ​

VariableCLI FlagDescription
STORYBOOK_DEPLOYER_DIR--dirBuild directory
STORYBOOK_DEPLOYER_PROJECT--projectProject ID
STORYBOOK_DEPLOYER_VERSION--versionVersion string
STORYBOOK_DEPLOYER_API_KEY--api-keyAPI key
STORYBOOK_DEPLOYER_API_URL--api-urlAPI endpoint

TIP

The SCRY_ prefix also works for all variables and takes precedence.

Troubleshooting ​

"Workflow not triggered" ​

Check that:

  1. Workflow file is in .github/workflows/
  2. Branch name matches trigger conditions
  3. GitHub Actions is enabled for the repository

"Secrets not available" ​

For pull requests from forks, secrets are not available by default. Consider:

  • Using environment-based secrets
  • Requiring approval for fork PRs

"Build fails" ​

Check that:

  1. build-storybook script exists in package.json
  2. Dependencies are correctly installed
  3. Node.js version matches your project requirements

Next Steps ​