storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
84.26k stars 9.27k forks source link

How to deploy storybook into a subpath #1291

Closed jantonso closed 7 years ago

jantonso commented 7 years ago

I'd like to be able to deploy storybook into the subpath of /storybook, such that I can target everything related to storybook in an nginx config to use my local domain of "https://test.dev".

I read that this is possible in the documentation, but couldn't get it working. If i extend the webpack config like so:

   module.exports = (storybookBaseConfig, configType) => {

    storybookBaseConfig.output.publicPath = '/storybook/';

    // Return the altered config
    return storybookBaseConfig;

};

It works for the "manager.bundle.js" and "preview.bundle.js", but not for "iframe.html" or webpack hot reloading or local css imported via absolute paths.

Is there an easy way to make sure that all requests related to running storybook get prefixed with /storybook?

Alternatively, is there a way to run storybook w/ https via the cli? I can set the host to 'test.dev, but I need it to be https for it to work in dev.

danielduan commented 7 years ago

If I'm understanding correctly, you'd like test.dev/storybook to be your static build of storybook.

You can just put everything inside /storybook in your root nginx server directory. You can use build-storybook -o file_path to set the directory: https://storybook.js.org/basics/exporting-storybook/

Unfortunately, running https requires certificate generation and it's not on our roadmap.

If there's still an issue, please reopen this.

weslleyaraujo commented 6 years ago

I am facing the same issue, my storybook is deployed to /storybook (using webpack publicPath) but the iframe wont follow

so what I get is: screen shot 2018-05-03 at 2 43 13 pm

if I manually replace the iframe url to /storybook/iframe.html everything works as expected, currently there is no way to control the iframe public path right?

Hypnosphi commented 6 years ago

@weslleyaraujo our publicPath is '' by default: https://github.com/storybooks/storybook/blob/master/app/react/src/server/config/webpack.config.prod.js#L34

This should allow you to serve storybook-static dir on any route without changing the default config

ozio commented 5 years ago

the link is broken, can you update it, please?

EvanLovely commented 4 years ago

@ozio It looks like it's probably this one: https://github.com/storybookjs/storybook/blob/ecaa84cd889bee6850c50bf4130a479e28aee88b/lib/core/src/server/preview/iframe-webpack.config.js#L124

The base webpack config might be helpful to link too: https://github.com/storybookjs/storybook/blob/ecaa84cd889bee6850c50bf4130a479e28aee88b/lib/core/src/server/preview/base-webpack.config.js

menosprezzi commented 3 years ago

Hello! Facing the same problem here! No config option and no documentation found about how to accomplish that on Storybook or even mentioning that use-case, sadly.

In Nextjs, for instance, we have the basePath setting that done it.

I've also tried to set config.output.publicPath = '/react/'; on webpack but it isn't worked.

image

Do you guys know a way to config it?

ryudice commented 3 years ago

Facing same issue also. Any ideas?

jowo-io commented 3 years ago

same here!

tobiloeb commented 3 years ago

Same here! Need this feature

logitimate commented 3 years ago

same!

yurist38 commented 3 years ago

This an absolute MUST HAVE! 🙌 Please add this possibility to specify a custom path prefix...

tobiloeb commented 3 years ago

How can we reopen this issue? Its closed but never solved.

yurist38 commented 3 years ago

I've just figured this out finally! 🎉 To set the path prefix you need to set publicPath in both webpackFinal and managerWebpack functions in the .storybook/main.js like this:

// .storybook/main.js
module.exports = {
  webpackFinal: async (config, { configType }) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
  managerWebpack: async (config) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
};

Also, I had to build Storybook with the preview-url parameter to make it find the iframe file:

build-storybook -- --preview-url=/my-prefix/iframe.html

For some reason, I have found nothing about it in official docs, had to google it a lot...

yannickcornaille commented 3 years ago

It doesn't work for me if I want to deploy Storybook into a subpath. Like other people here I use Next.js and we have to serve static files from the root / as described here https://nextjs.org/docs/basic-features/static-file-serving

So in my components for an image myImage.png in the /public folder I have this :

<img alt="" src="/myImage.png" />

Everything is fine if I deploy Storybook at the root of the site but not into a subpath (/storybook). I tried :

Unfortunately nothing allowed me to get the assets to work. Do you have any other ideas?

tobiloeb commented 3 years ago

I've just figured this out finally! 🎉 To set the path prefix you need to set publicPath in both webpackFinal and managerWebpack functions in the .storybook/main.js like this:

// .storybook/main.js
module.exports = {
  webpackFinal: async (config, { configType }) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
  managerWebpack: async (config) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
};

Also, I had to build Storybook with the preview-url parameter to make it find the iframe file:

build-storybook -- --preview-url=/my-prefix/iframe.html

For some reason, I have found nothing about it in official docs, had to google it a lot...

Thanks a lot, this works perfect for me!

JAMesserman commented 3 years ago

I've just figured this out finally! 🎉 To set the path prefix you need to set publicPath in both webpackFinal and managerWebpack functions in the .storybook/main.js like this:

// .storybook/main.js
module.exports = {
  webpackFinal: async (config, { configType }) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
  managerWebpack: async (config) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
};

Also, I had to build Storybook with the preview-url parameter to make it find the iframe file:

build-storybook -- --preview-url=/my-prefix/iframe.html

For some reason, I have found nothing about it in official docs, had to google it a lot...

Thanks a lot, this works perfect for me!

Still not working for me... what are you guys using to serve your static files / whats your server config like?

tobiloeb commented 3 years ago

I've just figured this out finally! 🎉 To set the path prefix you need to set publicPath in both webpackFinal and managerWebpack functions in the .storybook/main.js like this:

// .storybook/main.js
module.exports = {
  webpackFinal: async (config, { configType }) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
  managerWebpack: async (config) => {
    config.output.publicPath = '/my-prefix/';
    return config;
  },
};

Also, I had to build Storybook with the preview-url parameter to make it find the iframe file:

build-storybook -- --preview-url=/my-prefix/iframe.html

For some reason, I have found nothing about it in official docs, had to google it a lot...

Thanks a lot, this works perfect for me!

Still not working for me... what are you guys using to serve your static files / whats your server config like?

In my case, I serve the storybook in a subpath of a springboot application. Whats not working for you in detail? Do the paths changed in the storybook index.html? From "/runtime\~main.xxxxx.bundle.js" to "/my-prefix/runtime\~main.xxx.bundle.js"? Same for the iframe.html

yurist38 commented 3 years ago

Here we come again... After upgrade to the latest storybook, the approach with specifying preview-url as a CLI parameter seems not working anymore. Trying to make it work but no luck so far. It's totally frustrating that this case is not covered in the storybook anyhow. Such a basic functionality as a building with a path prefix must be supported! Guys, do something with it, please...

l2obin commented 3 years ago

Here we come again... After upgrade to the latest storybook, the approach with specifying preview-url as a CLI parameter seems not working anymore. Trying to make it work but no luck so far. It's totally frustrating that this case is not covered in the storybook anyhow. Such a basic functionality as a building with a path prefix must be supported! Guys, do something with it, please...

I am also facing this problem. I managed to get it working temporarily for the time being by manually adding window['PREVIEW_URL'] = '/my-prefix/iframe.html'; in the generated index.html within script tag right after div#docs-root.

It will look something like this:

<div id="docs-root"></div>
<script>
    window['CONFIG_TYPE'] = "PRODUCTION";
    window['PREVIEW_URL'] = "/my-prefix/iframe.html"; // HOTFIX
    ...
</script>
<script src="/my-prefix/runtime~main.XXXXX.manager.bundle.js"></script>

Anyone have a better solution?

a-zhelonkin commented 3 years ago

I am using build-storybook option -o path/to/my-prefix, main.js config file like this and manager-head.html file with content

<link rel="shortcut icon" type="image/x-icon" href="/my-prefix/favicon.ico">
<script>
    window['PREVIEW_URL'] = '/my-prefix/iframe.html';
</script>

It's working for me now with 6.3.4 version

billiegoose commented 2 years ago

Thank you everyone who has helped provide instructions for how to get this working. I'll contribute my own small improvement, which I used to serve storybook from /my-prefix when built with build-storybook but still serve it from the root when run with start-storybook:

  1. the storybook/main.js

    module.exports = {
    // See https://github.com/storybookjs/storybook/issues/1291#issuecomment-795251283
    webpackFinal: async (config, { configType }) => {
    if (configType === 'PRODUCTION') {
      config.output.publicPath = '/my-prefix/';
    }
    return config;
    },
    managerWebpack: async (config, { configType }) => {
    if (configType === 'PRODUCTION') {
      config.output.publicPath = '/my-prefix/';
    }
    return config;
    },
    };
  2. a .storybook/manager-head.html:

    
    <!-- See https://github.com/storybookjs/storybook/issues/1291#issuecomment-891379856 -->
    <link id="favicon" rel="shortcut icon" type="image/x-icon" href="/my-prefix/favicon.ico" />
    <script>
    if ('%NODE_ENV%' === 'production') {
    window['PREVIEW_URL'] = '/my-prefix/iframe.html';
    } else {
    document.getElementById('favicon').setAttribute('href', '/favicon.ico');
    }
    </script>
Lootjs commented 2 years ago

if you using Vite + Storybook, then that should be helpful

  viteFinal: (config, { configType }) => {
    // some configs
    if (configType === 'PRODUCTION') {
      config.base = '/my-prefix/';
    }

    return config
  },
MXTcomunica commented 2 years ago

Some transformations can be done directly in managerHead, without the need to use .storybook/manager-head.html.

See my example here: https://github.com/storybookjs/storybook/issues/7775#issuecomment-968992047

leandrosrocha commented 2 years ago

I am using Storybook Vite and this config worked for me:

module.exports = {
  viteFinal: (config, { configType }) => {
    if (configType === 'PRODUCTION') {
      config.base = './';
    }

    return config;
  },
  ...
};
retrohacker commented 2 years ago

Using a fresh install of create-react-app and storybook-init I was able to serve storybook from /storybook/ using:

./storybook/main.js

module.exports = {
  ... [LEAVE WHAT IS ALREADY HERE ALONE, ADD THE NEXT TWO FUNCTIONS] ...
  webpackFinal: async config => {
    config.output.publicPath = '/storybook/'
    return config
  },
  managerWebpack: async config => {
    config.output.publicPath = '/storybook/'
    return config
  }
}

./manager-head.html

(You'll need to create this file)

<script>
  window['PREVIEW_URL'] = '/storybook/iframe.html'
</script>

(I'm using fastify and @fastify/fastify-http-proxy to do the proxying)

emosheeep commented 1 year ago

Summaries of above

After above, you will find that your storybook main frame appears but the stories(iframe.html) doesn't. You should set global var window.PREVIEW_URL with '${your subpath}/iframe.html', how to set it was referred above, you can create a new file named manager-head.html and write a script to modify or use managerHead function.

Examples

Before deploying your websites to a subpath like https://example.com/storybook/:


const base = '//cdn.example.com/storybook/'; // we usually upload assets to CDN

module.export = {
  ...
  // This is to change configurations of building process of storybook's main frame
  managerWebpack: (config, { configType }) => {
    if (configType === 'PRODUCTION') {
      config.output.publicPath = base;
    }
    return config;
  },
  managerHead: (head, { configType }) => {
    const injections = [
      `<link rel="shortcut icon" type="image/x-icon" href="${base}favicon.ico">`, // This set icon for your site.
      `<script>window.PREVIEW_URL = '${base}iframe.html'</script>` , // This decide how storybook's main frame visit stories 
    ]
    return configType === 'PRODUCTION'
      ? `${head}${injections.join('')}`
      : head
  },
  // Or webpackFinal
  async viteFinal(config, { configType }) {
    if (configType === 'PRODUCTION') {
      config.base = base
    }
    return config;
  }
  ...
}
brunoreis commented 1 year ago

The app I'm working at uses Nextjs and we're using the nextjs framework: https://github.com/storybookjs/storybook/tree/next/code/frameworks/nextjs

We've set up storybook to run in a subpath using nginx:

server {
    listen 80;
    server_name www.[domain].local.com.br;
    location / {
        proxy_pass http://0.0.0.0:3000/;        
    }
    location /storybook/ {
        proxy_pass http://0.0.0.0:6006/;   
    }
}

and adding that entry to /etc/hosts

127.0.0.1   www.[domain].local.com.br

After doing these initial steps, the stories were correctly rendering under /storybook in my local domain. The only missing part was that the HMR was trying to load __webpack_hmr at the root and not at the /storybook subpath, thus the stories were not getting updated when files were changed.

I was able to fix that by overriding the entry webpack config, adding these lines to main.js:

const absolutePath = path.resolve(__dirname + '/../')
module.exports = {
    //...
    webpackFinal: async (config) => {
        config.entry = [
            `${absolutePath}/node_modules/webpack-hot-middleware/client?reload=true&path=/storybook/__webpack_hmr`,
            `${absolutePath}/storybook-config-entry.js`,
        ]
        return config
    },
rapidfixer commented 1 year ago

I suppose managerWebpack and managerHead are not supported anymore at v7?

AlanMorel commented 1 year ago

Yeah what are we supposed to do on v7?

PsyGik commented 1 year ago

FWIW, I ran into this on v7 and had to do this instead of the webpack configs.

// package.json
"build:storybook": "storybook build --disable-telemetry",
"postbuild:storybook": "sed -i 's#<head>#<head>\\n<base href='\/storybook\/'>#' ./storybook-static/index.html"

What it essentially does is to add <base href='/storybook/'> after the <head> tag.

I tried using managerHead like so:

managerHead: (head, { configType }) => {
    if (configType === 'PRODUCTION') {
      return (`
        <base href="/storybook/">
        ${head}
      `);
    }
  },

but for some reason, the <base href="/storybook/"> was always appended after all the <link> tags, which made it effectively useless.

If either of the following ...(base href="/storybook/")... attributes are specified, this element must come before other elements with attribute values of URLs, such as 's href attribute. - MDN

My use-case was to deploy to a S3 bucket fronted by a CDN that opens Storybook on https://example.com/storybook. I had to write additional routing logic on the CDN layer to route /storybook requests to the right S3 bucket (which is beyond the scope for this discussion)

andrecalil commented 1 year ago

This issue is from 2017. It's 2023 and I can't figure out how to host Storybook on a path other than root. It deeplinks all the assets to ./, I've not been able to find a way to tell it to use ./whatever/ instead.

AlanMorel commented 1 year ago

Yeah my previous comment was on April 24, it's now almost October and there's still no clear cut solution for v7. I don't think it's a priority at all (which is crazy because deploying on a subpath is extremely useful)

conor-ob commented 11 months ago

FWIW, I ran into this on v7 and had to do this instead of the webpack configs.

// package.json
"build:storybook": "storybook build --disable-telemetry",
"postbuild:storybook": "sed -i 's#<head>#<head>\\n<base href='\/storybook\/'>#' ./storybook-static/index.html"

What it essentially does is to add <base href='/storybook/'> after the <head> tag.

I had spent hours on this on v7 until this solution pointed me in the right direction. The sed command wasn't working for me so I went with a variation using perl

"build:storybook": "storybook build",
"postbuild:storybook": "perl -i -p -e 's#<head>#<head>\n\t\t<base href='/storybook/'>#' ./storybook-static/index.html"
bab2683 commented 9 months ago

FWIW, I ran into this on v7 and had to do this instead of the webpack configs.

// package.json
"build:storybook": "storybook build --disable-telemetry",
"postbuild:storybook": "sed -i 's#<head>#<head>\\n<base href='\/storybook\/'>#' ./storybook-static/index.html"

What it essentially does is to add <base href='/storybook/'> after the <head> tag.

I had spent hours on this on v7 until this solution pointed me in the right direction. The sed command wasn't working for me so I went with a variation using perl

"build:storybook": "storybook build",
"postbuild:storybook": "perl -i -p -e 's#<head>#<head>\n\t\t<base href='/storybook/'>#' ./storybook-static/index.html"

This worked for me, thanks a lot @conor-ob !