diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 955b1a17c7cb8dcfc4757758fd68e5373155eceb..03c0fab6f8b5d9cb621bd80de9a6fe4a21b497a8 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -353,8 +353,6 @@ compile-storybook:
     - cd storybook/
     - run_timed_command "retry yarn install --frozen-lockfile"
     - yarn build
-    - mkdir public
-    - mv storybook-static/* public
   artifacts:
     name: storybook
     expire_in: 31d
diff --git a/app/assets/javascripts/vue_shared/components/todo_button.stories.js b/app/assets/javascripts/vue_shared/components/todo_button.stories.js
index d85945433afdece718bc55c25ba80cb84ea425fc..db4d8724a0d9f6dd8a0ec7b78231d9bd56cad900 100644
--- a/app/assets/javascripts/vue_shared/components/todo_button.stories.js
+++ b/app/assets/javascripts/vue_shared/components/todo_button.stories.js
@@ -4,7 +4,7 @@ import TodoButton from './todo_button.vue';
 
 export default {
   component: TodoButton,
-  title: 'components/todo_button',
+  title: 'vue_shared/components/todo_button',
 };
 
 const Template = (args, { argTypes }) => ({
diff --git a/doc/development/fe_guide/storybook.md b/doc/development/fe_guide/storybook.md
new file mode 100644
index 0000000000000000000000000000000000000000..2d085749c33ecbdcd728321a0f7c9c31b0a1f8ea
--- /dev/null
+++ b/doc/development/fe_guide/storybook.md
@@ -0,0 +1,44 @@
+# Storybook
+
+The Storybook for the `gitlab-org/gitlab` project is available on our GitLab Pages site at https://gitlab-org.gitlab.io/gitlab/storybook.
+
+## Storybook in local development
+
+Storybook dependencies and configuration are located under the `storybook/` directory.
+
+To build and launch Storybook locally, in the root directory of the `gitlab` project:
+
+1. Install Storybook dependencies:
+
+    ```bash
+    yarn storybook:install
+    ```
+
+1. Build the Storybook site:
+
+    ```bash
+    yarn storybook:start
+    ```
+
+## Adding components to Storybook
+
+Stories can be added for any Vue component in the `gitlab` repository.
+
+To add a story:
+
+1. Create a new `.stories.js` file in the same directory as the Vue component.
+   The file name should have the same prefix as the Vue component.
+
+    ```txt
+    vue_shared/
+    ├─ components/
+    │  ├─ todo_button.vue
+    │  ├─ todo_button.stories.js
+    ```
+
+1. Write the story as per the official Storybook instructions: https://storybook.js.org/docs/vue/writing-stories/introduction
+   
+   Notes:
+   - Specify the `title` field of the story as the component's file path from the `javascripts/` directory,
+     e.g. if the component is located at `app/assets/javascripts/vue_shared/components/todo_button.vue`, specify the `title` as
+     `vue_shared/components/To-do Button`. This will ensure the Storybook navigation maps closely to our internal directory structure.
diff --git a/package.json b/package.json
index 903c310927dc51e965d056b48f96511cf0f48ecd..cd284bebbff4e6e5173afbd98fbc15d91116a1a8 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,12 @@
     "markdownlint:no-trailing-spaces": "markdownlint --config doc/.markdownlint/markdownlint-no-trailing-spaces.yml",
     "markdownlint:no-trailing-spaces:fix": "yarn run markdownlint:no-trailing-spaces --fix",
     "postinstall": "node ./scripts/frontend/postinstall.js",
+    "storybook:install": "yarn --cwd ./storybook install",
+    "storybook:start": "yarn --cwd ./storybook start",
     "stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js",
     "webpack": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.config.js",
     "webpack-vendor": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.vendor.config.js",
-    "webpack-prod": "NODE_OPTIONS=\"--max-old-space-size=3584\" NODE_ENV=production webpack --config config/webpack.config.js",
-    "storybook": "yarn --cwd ./storybook start"
+    "webpack-prod": "NODE_OPTIONS=\"--max-old-space-size=3584\" NODE_ENV=production webpack --config config/webpack.config.js"
   },
   "dependencies": {
     "@babel/core": "^7.10.1",
diff --git a/storybook/.gitignore b/storybook/.gitignore
index 71be6bdbd43d2fb9ca03ce8514c3023db7a39a15..18d5e871aaa49e9acd4a773368fc686fd29c2494 100644
--- a/storybook/.gitignore
+++ b/storybook/.gitignore
@@ -1,2 +1,2 @@
 node_modules/
-storybook-static
\ No newline at end of file
+public/
\ No newline at end of file
diff --git a/storybook/config/main.js b/storybook/config/main.js
index 72fba9568c799971ab9a4973f70a4a92d43201e8..bf88af173e33bcaf3739190b3b9a2bde7eea77f3 100644
--- a/storybook/config/main.js
+++ b/storybook/config/main.js
@@ -1,5 +1,8 @@
 /* eslint-disable import/no-commonjs */
 module.exports = {
-  stories: ['../../app/assets/javascripts/**/*.stories.js'],
+  stories: [
+    '../../app/assets/javascripts/**/*.stories.js',
+    '../../ee/app/assets/javascripts/**/*.stories.js',
+  ],
   addons: ['@storybook/addon-essentials', '@storybook/addon-a11y'],
 };
diff --git a/storybook/package.json b/storybook/package.json
index b3db4111b7252d7d3229678ede8a4d782a7ee98a..028133dd5e374e2fe95fa855dbb5f2c51a2bf63d 100644
--- a/storybook/package.json
+++ b/storybook/package.json
@@ -2,8 +2,8 @@
   "private": true,
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
-    "start": "start-storybook -p 6006 -c config",
-    "build": "build-storybook -c config"
+    "start": "start-storybook -p 9002 -c config",
+    "build": "build-storybook -c config -o public"
   },
   "dependencies": {},
   "devDependencies": {