In the previous article of this series, we saw how to create a Next.js app with Tailwind and Storybook on a Stackomate dev-container. Now, we will see some common patterns and improvements that can be used to make your development workflow easier.
Avoid Import Path Issues
⚠️ It seems that using the default @
alias for Next.js
may create some problems when importing such files in the Storybook server. Replacing them with relative paths, such as ..
, seem to have fixed the issues. We may revisit this issue in the future for further investigation.
Add Support for SVG Imports
One of the quickest ways to add support for SVG imports is to use the svgr
cli. The cli will transpile SVG images into React components which can be used directly in your code.
- Create a folder in the
src/
directory of your Next.js project calledsvgr
; - Move all of your SVG images to the
src/svgr
folder; - Optionally install the
@svgr/cli
package as adevDependency
:
npm install --save-dev @svgr/cli
- Run
npx -y @svgr/cli --out-dir src/svgr -- src/svgr
;
ℹ️ You should re-run this command whenever a new SVG file is added to the src/svgr
folder.
ℹ️ You may also create a npm script in the package.json
file to automate calling the command above, such as:
{
"scripts": {
"svgr": "npx -y @svgr/cli --out-dir src/svgr -- src/svgr"
}
}
- Import the generated javascript files from the
src/svgr
folder into your project:
import MyCustomIcon from '../svgr/my-custom-icon'
Use the clsx
Library (Recommended)
The clsx
library facilitates the construction of conditional classes, by receiving strings, objects and arrays. Here are some examples taken from its documentation:
import clsx from 'clsx';
clsx('foo', true && 'bar', 'baz');
clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });
clsx(['foo', 0, false, 'bar']);
To install it, run npm install --save clsx
in your application root folder.
Use the twMerge
Library (Optional)
The twMerge
(tailwind-merge
) library "merges Tailwind CSS classes without style conflicts" source. Here is an example taken from its documentation:
import { twMerge } from 'tailwind-merge'
twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
// → 'hover:bg-dark-red p-3 bg-[#B91C1C]'
To install it, run npm install --save tailwind-merge
in your application root folder. The maintainers of twMerge
have also made a guide explaining about when and how to use it.
Manage Fonts with @next/fonts
Using @next/fonts
includes benefits such as latency reduction and enhanced privacy. It is also built similarly to the component architecture used by React.
- Install Google Fonts with Next.js;
- Use a shared file to host fonts;
- How to use Next Fonts in Storybook.
Our Recommendation:
- Create a
src/fonts
folder and anindex.tsx
file, and export all fonts in such file.
// src/fonts/index.tsx
import { Rubik_Iso } from 'next/font/google'
import { Inter } from 'next/font/google'
export const rubikIso = Rubik_Iso({ subsets: ['latin'], weight: "400" });
export const inter = Inter({subsets: ['latin']})
- Consume the exported fonts in your components
// src/stories/Header.tsx
import { rubikIso } from '../fonts';
// ...
export const Header = () => (
<header>
<div className={clsx(`bg-red-500 dark:bg-yellow-600`, rubikIso.className)}>
// ...
Create a SharedDefaults
Component
Creating a SharedDefaults
component will provide a common component to be used both by Next.js and Storybook servers. This is a good strategy to declare fonts and themes, for example, as they can be referenced in your App
component and stories.
- Create a
src/components
folder; - Create a
SharedDefaults.tsx
file which acceptschildren
props;
ℹ️ The SharedDefaults
component may use Tailwind classes and @next/fonts
. For example:
// src/components/SharedDefaults.tsx
import { rubikIso } from "../fonts";
import clsx from "clsx";
import { ReactNode } from "react";
export const SharedDefaults = ({children}: {children: ReactNode}) => {
return <div className={clsx("bg-purple-800 text-white", rubikIso.className)}>
{children}
</div>
}
- Wrap your Next.js components with the
SharedDefaults
component.
// src/app/page.tsx
import { SharedDefaults } from '@/components/SharedDefaults'
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<SharedDefaults>
<div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
// ...
</div>
</SharedDefaults>
</main>
)
}
- Wrap Storybook story components with
SharedDefaults
.
ℹ️ Instead of wrapping each stories
component files in a SharedDefaults
component, we recommend declaring the latter as a Storybook Decorator for context-mocking in the .storybook/preview.tsx
file:
- Stop the storybook server;
- Rename the
.storybook/preview.ts
file to use.tsx
extension; - Append the
SharedDefaults
wrapper to thedecorators
property (and declare aReact
import explicitly):
// .storybook/preview.tsx
import React from 'react';
import { SharedDefaults } from '../src/components/SharedDefaults';
// ...
decorators: [
// ... ,
(Story) => (
<SharedDefaults>
<Story />
</SharedDefaults>
),
]
ℹ️ If you don't see the SharedDefaults
fonts/colors being applied in Storybook, it may be because the specific component overrides the font in its classes. For example, the Header.tsx
component contains a storybook-header
class that uses the Nunito Sans
font. To verify that SharedDefaults
fonts and themes are being applied, remove the conflicting classes from the className
in the targeted components, and try again.
Conclusion
Although Next.js, Tailwind and Storybook have come a long way regarding mutual integration, some of the challenges mentioned in this article can arise for the developers. By applying our suggestions, it is possible to reduce the amount of work required in future phases of development, while still maintaining a consistent workflow.
Top comments (0)