CSS

Summary

TypeScript + tooling component library with CSS added.

As we're now using tsup for building our library, adding CSS becomes very comfortable, as tsup supports this natively (through esbuild).

Rearranging

But first, we're going to do some quality-of-life improvements by preparing a separation of components:

  • add a new file, src/button/index.tsx and move the button component there.
  • replace src/index.tsx with just export * from './button/index.tsx.

We're using nodenext for our module resolution in tsconfig.json, which requires us to use file extensions for our imports. But TypeScript doesn't like this by default - we have to set allowImportingTsExtensions in our tsconfig first do to that, which also requires us to set noEmit.

You could also set your module resolution to node and do export * from './button instead. If we'd use tsc to compile our library, we would have to do export * from './button/index.js and therefore reference a non-existent file. There's a long explanation by the TS team as to why this is a sensible decision. We're using tsup to build our library, so setting noEmit is not a problem.

Adding CSS

Create a src/button/styles.css that includes something like this:

.button {
  background: steelblue;
  color: white;
  border: none;
}

We can then import those styles into our component by doing import './styles.css':

import { type PropsWithChildren } from 'react'
import './styles.css'
 
export const Button: React.FC<PropsWithChildren> = ({ children }) => (
  <button className="button">{children}</button>
)

If we now run pnpm build, you will see an index.css in your dist folder.

If you want, you can additionally add a global stylesheet by creating src/styles.css and referencing it in src/index.tsx:

import './styles.css'
 
export * from './button/index.tsx'

To avoid clashes between the library and application CSS, I would recommend to not style tags directly and use CSS classes instead - ideally prefixing them in some way, e.g. .acme-ui-button.

Using the generated CSS

When consuming your library, you also need to import the generated stylesheet from dist/index.css. In Next.js, you would typically do this in _app.tsx.

import '@/styles/globals.css'
import 'our-library/dist/index.css'
 
/* ... */

The order of CSS imports matters. What the "correct" order is depends a bit on your setup - usually, your application will do a CSS reset, therefore importing your library's CSS after your application's CSS might be sensible.

To make importing the styles a bit nicer, we can replace the main field with an exports field to our library's package.json file:

{
  "exports": {
    ".": "./dist/index.js",
    "./styles": "./dist/index.css"
  },
}

Afterwards, we can import the CSS like this:

import '@/styles/globals.css'
import 'our-library/styles'
 
/* ... */