In this tutorial we will see how to setup a reproducible web development stack containing Next.js, Storybook and Tailwind inside a dev-container.
Using a dev-container provides a reproducible environment for your team, and guarantees the isolation of your project files. For example, different projects may contain different Node.js and/or Python versions. We will be using the Stackomate CLI, a global npm package capable of generating templates for development containers for variable purposes.
Creating a Dev-Container
First, let's create our new dev-containers with the Stackomate CLI by running
npx -y stackomate@latest dev-container create
and following the default instructions. You will be prompted to select a container type. Below we explain the differences between two common options:
Option 1 - Node.js Dev-Container
You may select the node
dev-container to achieve smaller image sizes and faster startup times. The developer can connect to the container via the terminal.
⚠️ Specifically for our web stack, internal ports 3000
and 6006
will be used by Next.js and Storybook servers, respectively, and thus they must be published to the host. This can be achieved by adding the following lines to the dev.docker-compose.yml
file created by the stackomate cli:
ports:
- 3000:3000
- 6006:6006
expose:
- 3000
- 6006
Option 2 - Full Desktop Dev-Container
The desktop-web
type contains a Ubuntu desktop interface with Node.js and a built-in reverse proxy installed. The developer can access Firefox, a default terminal and a simple file manager on the browser. It is a great choice for larger projects requiring complex configuration, such as website stacks requiring the generation of local SSL certificates. The desktop interface will be published internally on port 6080
, by default. If you need another port, you can change the following lines in the generated dev.docker-compose.yml
file:
ports:
- 6080:6080
expose:
- 6080
ℹ️ Note: you may create both container types for your web stack, if desired. You may also run them both at the same time.
Once you have created and edited your dev-containers, follow instructions in the generated README.md
files to start them. For example, the node
dev-container can be started by running cd ./dev-container/node && ./dev.sh
(or ./sudo-dev.sh
, if your Docker installation requires sudo
permissions).
Creating Your Next.js App
Now that we have our container running, it is time to start creating our Next.js app inside of it:
- Access
/app
inside your container terminal:cd /app
; - Call the
create-next-app
cli:npx -y create-next-app@latest
;
We recommend using the following options, except for the project name, which may be chosen arbitrarily. With these settings, Next.js will spare us from installing Tailwind and ESLint separately, and our project structure will contain a src
folder and the latest App
Router:
What is your project named? › my-test-app
Would you like to use TypeScript? › No / (Yes)
Would you like to use ESLint? › No / (Yes)
Would you like to use Tailwind CSS? › No / (Yes)
Would you like to use `src/` directory? › No / (Yes)
Would you like to use App Router? (recommended) › No / (Yes)
Would you like to customize the default import alias? › (No) / Yes
ℹ️ If you ever want to add Tailwind to an existing Next.js project, you can follow their official guide for more instructions.
⚠️ If you have created the Next.js project without the src
folder, please make sure to update all imports in the next parts of this article to reflect the folder structure changes. For example, the src/app/globals.css
file would otherwise be located in app/globals.css
.
- Wait for the bootstrapping process to be complete;
-
cd /app/my-test-app
;
ℹ️ You can ignore linting warnings at the ./src/app/globals.css
file, such as Unknown at rule @tailwindcss(unknownAtRules)
. If you really want to fix it though, you can follow this solution to add customAt
rules.
Installing Storybook
We have Next.js and Tailwind installed, and now we must install Storybook and integrate all three:
- Run
npx -y storybook@latest init
ℹ️ Storybook should automatically detect the Next.js project type. It should also detect ESLint and ask:
We have detected that you're using ESLint. Storybook provides a plugin that gives the best experience with Storybook and helps follow best practices: https://github.com/storybookjs/eslint-plugin-storybook#readme
Would you like to install it? › (Y/n)
Choose Y to proceed with the Storybook ESLint Plugin installation.
Once Storybook has finished installing, it will automatically start on port 6006
.
- Go to your browser and open http://localhost:6006
Now Storybook has been seamlessly integrated with Next.js. You can check your package.json
file for a @storybook/nextjs
dependency. More details about all Next.js features supported by Storybook can be viewed on their package documentation page.
Adding Support for Tailwind on Storybook
Although Storybook has detected and integrated with our Next.js project, it has not detected Tailwind by default.
ℹ️ You can verify it by editing the src/stories/Header.tsx
file: add the bg-red-500
class to the element containing className="storybook-header"
. If the header background does not turn red, then Tailwind is not working with Storybook yet.
Now, we integrate Tailwind with Storybook:
- Include the
src/stories
directory in yourtailwind.config.js
, so that Tailwind can find and target React components in that folder:
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
'./src/stories/**/*.{js,ts,jsx,tsx,mdx}',
],
// ...
⚠️ Before proceeding, we recommend to commit your git changes in the project directory: for example, run git add -A
and git commit -m 'chore: add storybook and tailwind'
.
- Stop storybook, and install the
@storybook/addon-styling
package in your project:
npm i -D @storybook/addon-styling
- Now run a codemod to help with the integration:
npx addon-styling-setup
You should see an output like this:
=========================================
🧰 Configuring @storybook/addon-styling
=========================================
(1/3) Project summary
• Built with webpack
• Styled with tailwind
(2/3) .storybook/main.ts
• Registering @storybook/addon-styling.
(3/3) .storybook/preview.ts
• Adding import for withThemeByClassName decorator
• Adding import for stylesheet
• Adding withThemeByClassName decorator to config
✨ Done
- Go to
.storybook/preview.ts
, and adjust the line below:
/* TODO: update import to your tailwind styles file. If you're using Angular, inject this through your angular.json config instead */
import '../src/index.css';
to
/* Import for our tailwind global styles file */
import '../src/app/globals.css';
- Remove the styles generated by Next.js from the
src/app/globals.css
file, and just leave the Tailwind imports:
@tailwind base;
@tailwind components;
@tailwind utilities;
- Open your storybook again by running
npm run storybook
🎉 Now, you should now see the Header.tsx
component with the red background in the Header
story! 🎉
Integrating Tailwind Themes with Storybook
Tailwind can allow the user to change the appearance of a website using light and dark themes. However, some minor changes must be made if you want to toggle themes in the Storybook preview, which we will see next.
ℹ️ To verify support for dark themes, add dark:bg-yellow-600
to your Header.tsx
component, and then toggle from light theme
to dark theme
in the top dropdown many times. Notice how only one of the background colors is used. This happens because, by default, Tailwind "uses the prefers-color-scheme CSS media feature" (source). Instead, we want to toggle it programatically:
- Go to
tailwind.config.ts
and set thedarkMode
property to support manual toggling by class:
const config: Config = {
darkMode: 'class',
content: [
// ...
}
Now, you should see the Header.tsx
component change its background color from red to yellow depending on the theme you choose!
🎉 You can now develop your Next.js website with Tailwind and Storybook, happy coding! 🎉
ℹ️ Once you want to exit your dev-container, use CTRL+C
to exit the storybook server, and type exit
to stop and remove the development-container. Don't worry about your project files, they will not be lost as they are being mapped to the host 🙂.
If desired, we can further push our dev-containers to GitHub, and distribute them with other people in the team.
Conclusion
In this tutorial, we described a series of steps to create a complex project stack using Next.js, Tailwind and Storybook, along with some challenges and fixes to integrate the existing tools. We have used the Stackomate CLI to help us generate a dev-container and redistribute our development environment in a more predictable way. In the next articles, we will see further improvements that our stack can use to support more common features desired during development.
Top comments (0)