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/svgrfolder; - Optionally install the
@svgr/clipackage 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/svgrfolder 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/fontsfolder and anindex.tsxfile, 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/componentsfolder; - Create a
SharedDefaults.tsxfile which acceptschildrenprops;
ℹ️ 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
SharedDefaultscomponent.
// 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.tsfile to use.tsxextension; - Append the
SharedDefaultswrapper to thedecoratorsproperty (and declare aReactimport 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)