Announcing a new Vite + React + shadcn/ui monorepo project template as a pnpm workspace

Kevin Firko
Published:

I released new pnpm workspace (monorepo) project template for TypeScript projects featuring Vite, React, and shadcn/ui with shadcn CLI, under the MIT License.

The new fluid-tailwind plugin enables fluid-responsive styling that’s more efficient to implement and maintain vs. media queries with multiple breakpoints.

The included React app demonstrates how everything in the workspace comes together to implement deployable applications. It has a trendy “SaaSy” landing template with a light/dark theme and lazy-loaded routes powered by react-router.

Code: https://github.com/firxworx/vite-shadcn-workspace

Preview: https://vite-shadcn-workspace.pages.dev/

The shadcn CLI is set up to install components into the package at packages/react-ui. You can easily add new components to the workspace with a single command.

Layout components demonstrate how to use the ~ fluid responsive modifier added by fluid-tailwind to write fluid responsive styles.

Stock shadcn/ui components added via the CLI are minimally modified to provide you with a blank slate to work from. I included a few extras such as LinkButton that shares its variants with Button.

TailwindCSS Highlights

The tailwind configuration uses a pair of tailwind presets that provide a great foundation for a design system or shared component library that can support multiple applications.

Presets

@workspace/tw–preset-shadcn implements the shadcn/ui installation described by the official docs. It can be used standalone or in combination with other presets, and it also exports a factory function createShadcnPreset() for additional flexibility.

@workspace/tw-preset-workspace customizes the tailwind theme. It adds the fluid-tailwind plugin and includes tw-preset-shadcn in its presets array.

The workspace preset includes utility functions you can use to define a custom theme and achieve a level of customization beyond what’s supported by stock shadcn/ui components.

Working tsup config files are included in case you want to explore building the presets and offer a head start if you want to publish them as npm packages.

Customized cn() utility

The essential cn() utility function popularized by shadcn/ui combines the functionality of the ubiquitous clsx library with tailwind-merge to intelligently merge CSS class names.

The workspace cn() function is exported from a dedicated package @workspace/style that extends tailwind-merge to support the ~ fluid modifier enabled by the fluid-tailwind plugin.

Extending tailwind-merge to support additions and customizations to tailwind themes and utilities is a crucial step that many projects overlook and is not documented by shadcn/ui.

The @workspace/style package also exports a custom implementation of clsx alongside cn() which eliminates a dependency and provides further opportunities for customization.

Tailwind configuration

A caveat of using fluid-tailwind is that its ~ modifier requires a custom tailwind extractor that can only be specified in a tailwind config file and not within in presets. Check out apps/client/tailwind.config.ts to see how it’s integrated with the project.

The React app adds support for postcss and tailwind via vite.config.ts config file so a dedicated postcss.config.js is not required. This is a more powerful approach that enables you to use the full range of postcss plugins supported by Vite.

Astro devs should check out my earlier post The Best Way to Add Tailwind to an Astro Project for a similar approach with the Astro config.

shadcn CLI

Adding new components to the workspace under packages/react-ui is as easy as running:

pnpm shadcn add

A postshadcn script in the root package.json runs after the CLI command completes to:

  • automatically lint and format the new files with biome
  • run pnpm install in case any new dependencies were added
  • run a small bash script to fix shadcn CLI borking subpath imports

Always review every file generated or modified by the CLI.

Biome

Biome will auto-fix many issues and you should expect it to identify some that require your attention every time you add a component.

Do not be afraid of the sea of RED console output! Read it 😊

shadcn/ui components were only ever intended to serve as a starting point for further customization. The hundreds of open issues on the project’s GitHub page are a testament that you should expect to get your hands dirty.

Bash script

The bash script to fix subpath imports is at packages/react-ui/fix-shadcn-import-paths.sh

I coded the bash script to use sed in a way that is compatible with both linux (GNU) and MacOS (BSD) versions of the utility to save Apple fans a common source of headaches.

Details about the subpath-import-fix script and why its important are a little long so if you’re interested I wrote a separate post about it:

shadcn CLI Borks Subpath Imports and How to Work Around It.

The additional details in that post will come in handy when I file a bug report 🤓

The post may be of interest in case you want to modify the script or need to troubleshoot it, or if you are trying to get the shadcn CLI integrated into your own project.

TypeScript config

packages/react-ui/tsconfig.json includes a TypeScript paths definition for the # subpath import syntax. Although it is technically redundant and superceded by subpath imports there are two key reasons for this:

  • shadcn CLI currently requires a definition to be present
  • VSCode tooling does not have 100% support for subpath imports in monorepos

The benefit for VSCode is related to developer experience and for features like CTRL/CONTROL + click navigation to work as expected.

Documentation

The codebase is extensively commented and I do not assume significant prior knowledge of the tools and libraries in play.

Every package has its own README.md that explains its purpose and how it fits into the workspace.

The root README.md describes the workspace layout and how to get started.

While the detailed comments may seem overkill to some, I hope the extra detail and links to relevant docs are useful for those who are less familiar with any part of the stack.

They certainly help me when I come back to a stack that I haven’t had the opportunity to work on for a while!

Vite is a powerful tool that serves as the backbone of numerous leading frameworks including Astro ❤️, Vue, Svelte, Remix, Solid, Qwik, Vike, Lit, and more. Enjoy! 🚀