DEV Community

Rafael Calpena
Rafael Calpena

Posted on

Common Improvements to your Next.js, Tailwind and Storybook Stack

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 called svgr;
  • Move all of your SVG images to the src/svgr folder;
  • Optionally install the @svgr/cli package as a devDependency:
npm install --save-dev @svgr/cli
Enter fullscreen mode Exit fullscreen mode
  • 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"
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Import the generated javascript files from the src/svgr folder into your project:
import MyCustomIcon from '../svgr/my-custom-icon'
Enter fullscreen mode Exit fullscreen mode

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']);
Enter fullscreen mode Exit fullscreen mode

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]'
Enter fullscreen mode Exit fullscreen mode

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.

Our Recommendation:

  • Create a src/fonts folder and an index.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']})
Enter fullscreen mode Exit fullscreen mode
  • 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)}>
    // ...
Enter fullscreen mode Exit fullscreen mode

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 accepts children 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>
}
Enter fullscreen mode Exit fullscreen mode
  • 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>
  )
}
Enter fullscreen mode Exit fullscreen mode
  • 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 the decorators property (and declare a React import explicitly):
// .storybook/preview.tsx

import React from 'react';
import { SharedDefaults } from '../src/components/SharedDefaults';

// ...

  decorators: [
    // ... ,
    (Story) => (
      <SharedDefaults>
        <Story />
      </SharedDefaults>
    ),
  ]
Enter fullscreen mode Exit fullscreen mode

ℹ️ 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)