Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to deploy storybook into a subpath #1291

Closed
jantonso opened this issue Jun 15, 2017 · 38 comments
Closed

How to deploy storybook into a subpath #1291

jantonso opened this issue Jun 15, 2017 · 38 comments

Comments

@jantonso
Copy link

jantonso commented Jun 15, 2017

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
Copy link
Member

danielduan commented Aug 27, 2017

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
Copy link

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
Copy link
Member

@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
Copy link

ozio commented Dec 27, 2018

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

@EvanLovely
Copy link

@menosprezzi
Copy link

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
Copy link

ryudice commented Feb 2, 2021

Facing same issue also. Any ideas?

@jowo-io
Copy link

jowo-io commented Mar 3, 2021

same here!

@tobiloeb
Copy link

tobiloeb commented Mar 3, 2021

Same here! Need this feature

@logitimate
Copy link

same!

@yurist38
Copy link

yurist38 commented Mar 9, 2021

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

@tobiloeb
Copy link

tobiloeb commented Mar 9, 2021

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

@yurist38
Copy link

yurist38 commented Mar 10, 2021

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
Copy link

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 :

  • to configure the publicPath in webpackFinal and managerWebpack
  • to add the PUBLIC_URL=/storybook environment variable
  • to add "homepage": "." in the package.json
  • to add a "baseUrl": "./storybook" in the jsconfig.json
  • to add a <base> element in the html page but the base has no effect on absolute urls.

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

@tobiloeb
Copy link

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
Copy link

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
Copy link

tobiloeb commented Mar 31, 2021

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
Copy link

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
Copy link

l2obin commented May 31, 2021

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
Copy link

a-zhelonkin commented Aug 2, 2021

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

bakoe added a commit to bakoe/haeley-examples-storybook that referenced this issue Jan 18, 2022
@billiegoose
Copy link

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;
  },
};
  1. 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
Copy link

Lootjs commented Jun 6, 2022

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
Copy link

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

See my example here: #7775 (comment)

@leandrosrocha
Copy link

leandrosrocha commented Aug 16, 2022

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

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

    return config;
  },
  ...
};

@retrohacker
Copy link

retrohacker commented Aug 18, 2022

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
Copy link

Summaries of above

  • If you want to change the path of assets inside the index.html, you should edit managerWebpack whether you used webpack or vite. Because storybook's main frame is built with webpack, managerWebpack is used to change its configurations.

  • If you want to change the path of assets of iframe.html, you should edit webpackFinal or viteFinal according to which tool you're using.

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
Copy link

brunoreis commented Jan 6, 2023

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
Copy link

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

@AlanMorel
Copy link

Yeah what are we supposed to do on v7?

@PsyGik
Copy link

PsyGik commented Jul 27, 2023

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
Copy link

andrecalil commented Sep 30, 2023

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
Copy link

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
Copy link

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
Copy link

bab2683 commented Jan 19, 2024

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 !

@hooper-hc
Copy link

Yeah what are we supposed to do on v8?

@dziamid
Copy link

dziamid commented Dec 27, 2024

In V8 you can set base path by extending vite config:

#main.ts
const config: StorybookConfig = {
    ...
    async viteFinal(config) {
        const { mergeConfig } = await import('vite');

        return mergeConfig(config, {
            base: '/storybook/',
        });
    },
}
export default config

@Kludgy4
Copy link

Kludgy4 commented Jan 8, 2025

As far as I can tell, the Vite solution doesn't work on Angular storybooks because storybook still only supports the old webpack build system.

In the Storybook 8 announcement there was the suggestion that they will support the new build system in Storybook 9

We’re also working with the Angular core team to support new high-performance build options in a future version.

Until then, the perl index.html <base ...> postbuild solution works for me as a hack.

@toBeOfUse
Copy link

You should be able to use the publicPath setting in Webpack the same way, in the webpackFinal config entry:

https://webpack.js.org/guides/public-path/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests