Design System
Design System is a powerful tool that helps you set up a thorough, end-to-end, fully configured design system with best practices and automation for your projects. With this tool, you are bound to save countless days if not weeks of setup time, allowing you to focus on delivering great products faster.
Aim
Creating reusable and modular components is no longer a challenge these days especially with tools like shadcn. But, the process of creating a proper industry level design system is an arduous and time consuming task. That is exactly our aim i.e. to streamline the design system setup to cater reusabilty, consistency, flexibility, modularity and scalability.
Reusability: A centralized component library ensures that the components are not just reusable inside 1 app but across multiple apps.
Consistency: Centralizing a piece of code always brings in consistency as long as the appropriate version is used everywhere.
Flexibility: Comes with TailwindCSS as the styling solution OOTB and due to it being a utility-first CSS framework, gives room to other styling solutions like StyledComponents, Emotion etc.
Modularity: Use/import the components as and when you need them. Each component can be used as a separate shared package inside a monorepo or can be published as a NPM package and be used as a dependency in a different (apps) repository. Create headless and modular components with tools like radix-ui, headless ui etc.
Maintainability: Even when your projects expand and become more complex, your component library remains coherent and managable. Tools like Nx and the concept of workspaces makes it extremely easy to maintain a design system codebase.
Tools and Technologies
The generated design system will come with the following tools/technologies configured out of the box:
- Nx v19 for easy workflow and project management i.e. automatic versioning, changelogs and releases
- Storybook v8 for design system and UI library documentation
- TailwindCSS as the styling option
- Radix UI for creating custom component library
- Chromatic for storybook deployment, visual testing and reviewing
- Tailwind Config Viewer for token visualization
- CVA, Tailwind Merge for writing manageable and scalable components
- SVGO and SVGR for optimized svg components
- Hygen for building components, hooks, pages etc on the fly
- Vite v5 for building storybook and Tsup for ui components and packages
- clsx and tw for efficiently constructing tailwind classes
- Husky to enhance the git workflow and lint-staged to run linters against staged git files
- eslint v8, prettier, stylelint, commitlint, commitizen etc. to ensure the best practies and help you keep the code consistent
This is a highly opinionated tool. Learn more about customization options.
Usage
Install the package globally by running:
npm i -g @genesisx/design-system@latest
Go to the directory where you want to create a design system and run the below command:
ds
Provide inputs to the prompts. Following the questions asked:
- What is your ui design system going to be called? (scoped name is recommended)
- Do you want to provide an additional prefix? (applicable only for scoped naming conventions)
- Choose your package manager (pnpm or yarn is recommended)
When you run the cli, you will see some instructions logged on your terminal. If you are not sure about scoped names or prefixes, then click on this to read the complete block.
If you're about to start a new project, consider the following options to make an informed decision. Each approach offers different benefits and trade-offs based on your project's needs.
Independent Design System Setup: Use the tool to build an independent design system. You can then publish the UI components to NPM (or the registry of your choice) and install them in other repositories (monorepos) where your applications reside.
Pros:
- Modularity: Allows you to use the design system across multiple projects and repositories.
- Versioning: Easier to manage and control versions of the design system components.
- Separation of Concerns: Keeps the design system isolated from application-specific code, leading to cleaner and more maintainable projects.
Cons:
- Dependency Management: Ensuring all projects are using the correct version of the design system can be challenging.
Monorepo Setup: Use the tool to create a design system. You can then start creating react or nextjs apps using react-archetype in the same repository.
Pros:
- Centralized Management: All code (design system and applications) is in one place, simplifying dependency management and development workflows.
- Consistency: Ensures that all applications use the same version of the design system components.
- Shared CI/CD: Unified continuous integration and deployment pipelines can be set up for the entire monorepo.
Cons:
- Complexity: Monorepos can become complex to manage as the number of projects grows.
- Build Times: Larger codebases results in longer build and test times.
Folder structure
Within the polyrepo app, you will find a range of project files that serve various purposes and contribute to the overall structure and functionality of your application. These files are designed to simplify development and ensure the smooth operation of your polyrepo architectures.
(Click on the image to see it in a new tab)
Documentation
There are 3 components to the documentation provided:
Storybook
Storybook is a must-have tool as it provides a convenient environment for building, testing, and documenting components, making it easier to create robust and reusable elements. With this tool, you will get an advanced Storybook setup that is fully configured not only to document your components but also to support comprehensive project documentation.

In .storybook
, you will get a theme/getTheme.ts
which gives you the option to fully customize and rebrand the storybook app to make it look just like your other apps. You can make use of the CSS Variables created in tailwind.css
file and pass it to the computeStyle
function as follows:
colorPrimary: computeStyle("--color-primary"), // using css vars
// OR
colorSecondary: '#fff', // using hex
Visit Storybook's Theme quickstart or Theming Types for more information on the available options.
Other than the main.ts
, you will also get a manager.ts
file where you can manage your Storybook UI layout and a preview.tsx
file to control individual story's layout
Docs App
As part of your design system, you will get an MDX-powered docs app inside the docs directory. This can be used to document your design system, UI components, design tokens, best practices, guidelines, etc. The docs app comes with two helper components: Callout
and Card
. You can add more such helpers to make your documentation more appealing and easier to work with. The mdx
files added in the docs app will be picked up by Storybook when it is spun up.
Adding `remark` or `rehype` Plugins to Storybook
Tailwind Config Viewer
Tailwind Config Viewer is a local UI tool for visualizing your Tailwind CSS configuration file.
It is an optional component for your documentation. The docs app contains a TCV.mdx
file where your tailwind-config-viewer is embedded using an iframe
.

Run and Build Documentation
To run Storybook parallel to tailwind config viewer, use:
npm run sbtcv
Once the server is up and running, you can access Storybook by opening your web browser and navigating to http://localhost:6006
.
You can build the static files for production by running the following command:
npm run build:storybook
To keep tailwind config viewer separate from the docs, click here
Template Generator
Design System comes equipped with template generator engine like Hygen to speed up your component/package generation process.Try running the below command to start execute the cli tool:
npm run generate
As part of this, you will get a __templates
directory which contains all your templates and prompts. In __templates
, you will have a plop
directory which further contains 3 sub-directories, namely: template
, component
and package
. template
contains a prompt.js
which is where the generate
command will go to when executed. template
will then take the flow to either the prompt.js
file of component
or package
based on the input provided.
generate
underneath runs the following command:
npx hygen plop template
Here, plop
is the directory name inside __templates
, and tempate
takes it to the prompt.js
in plop/template
directory. If you want to add more such templates, follow the below steps:
Create a new directory inside
plop
, let's call itnewGen
. So, it will be as_templates/plop/newGen
.Add all the necessary template files ending with
.ejs.t
inside the newly created directory with appropriate path to be copied at.---
to: path/to/<%= newGen %>/index.js
---
console.log('Hello World');Create a
prompt.js
file with the required prompts.module.exports = [
{
type: "input",
name: "newGen",
default: "default",
message: "What is your newGen going to be called?",
},
];Add an additional object in the choices array in
_templates/plop/template/prompt.js
choices: [
{ name: "component", message: "A new component" },
{ name: "package", message: "A new package" },
{ name: "newGen", message: "A new generator" },
], // add more based on your requirement
Component library
In libs/ui
, you will find a starter ui kit with some sample components OOTB. This library follows the ATOMIC design principle to segregate and organize the ui components.
New Component
To generate a new component, run:
npm run generate
Select A new component
, provide the component name and the directory (atoms, molecules, organisms, templates) where you want it to be added.

Packages
In your packages
directory, you will be provided tailwind
and svg
package.
Tailwind
This package contains 4 sub-directories, namely: config
, styles
, themes
and utilities
.
config
- contains the basetailwind
andpostcss
config files. You can put all the necessary tailwind configuration here which you would need across all your apps.styles
- comes withnormalize.css
andtailwind.css
files.normalize.css
is the css reseter whiletailwind.css
contains the standard tailwind imports and your other design variables.utilities
- place where you will create your tailwind utilites.
SVG
This package provides support to convert the SVG files into react component. It uses svgo
to optimize the sbg files and svgr
to convert the optimized svg into a react component.
The react components can be imported in other apps through named import. Tree shaking is enabled in this package.
The recommended way of using small and medium size SVG is to optimize the svg and convert them into a react component.
But in case you are working with large number of SVG files or massive SVG files, it is recommended to host the optimized SVG files on some object store or CDN. To use the SVG hosted on CDN, we can fetch the url of the svg on CDN and then use it inside our react component. One such example is shown here https://github.com/Paul-Ayanava/cdn-svg-react-example .
New package
To create a new package, run:
npm run generate
Select A new package
and provide the package name.

By default, the ui library and packages are set up to be built and published to npm i.e. when design system is initialized, the build process for all the ui components and the packages are run by the cli. This setup is useful if you want to keep the design system and your applications in separate repositories.
However, if you prefer to keep the design system and your applications within the same monorepo, you do not need to build and publish the packages separately. Instead, you can leverage the benefits of workspaces. To do this, delete the dist folder and adjust the main
and exports
keys in all package.json
files to point to the actual .ts
files instead of dist/*.js
.
To learn more, check out this link.
Tailwind and PostCSS setup
To use tailwind in your apps, follow the below steps:
Install your published package or add
<tailwind-package-name>
in your app's devDependencies manually:apps/my-app/package.json// only if your design system is in the same repo (monorepo)
{
"devDependencies": {
"<tailwind-package-name>": "*",
//...
}
}You will have to have to create
tailwind.config.cjs
andpostcss.config.cjs
files in your app where you want to use tailwind.In
tailwind.config.cjs
, add the following snippet:tailwind.config.cjsconst { tailwindConfig } = require('<tailwind-package-name>/config');
module.exports = {
presets: [tailwindConfig],
content: [
...tailwindConfig.content,
// add this only if you are installing your ui component rather than using the symlinked version
'../../node_modules/<scopeName>/**/**/index.js'
// use "../../node_modules/<ui-component-name>/dist/index.js" if you are NOT working with scoped names
],
};In
postcss.config.cjs
, add the following snippet:postcss.config.cjsconst { postcssConfig } = require('<tailwind-package-name>/config');
module.exports = postcssConfig;Import
tailwind.css
in your app root (main.tsx or _app.tsx etc):/src/_app.tsximport '<tailwind-package-name>/styles/tailwind.css';
// ...Now, you should be able to use tailwind classes in your apps.
If you have custom CSS to add, create the necessary CSS file(s) and import these files at the very top of a global CSS file, followed by any other styles.
Other Scripts
Build
To run the build script present in all your workspaces (this does not include storybook and tcv):
npm run build
Release
Releases are equivalent to publishing the packages to NPM and this process is delegated to Nx.
First release:
# dry running first release
npm run release:d --first-release
# first release
npm run release --first-releaseSubsequent releases:
# dry running releases
npm run release:d
# release
npm run releasedangerThe --dry-run option is useful for testing your configuration without actually creating a release. It is recommended to run release once with --dry-run first to ensure everything is configured correctly.
Configure Custom Registries
To publish JavaScript packages, Nx Release uses the npm CLI under the hood, which defaults to publishing to the npm registry (https://registry.npmjs.org/). If you need to publish to a different registry, you can configure the registry in the .npmrc file in the root of your workspace or at the project level in the project configuration.
/.npmrcregistry=https://my-custom-registry.com/
//my-custom-registry.com/:_authToken=${REGISTRY_AUTH_TOKEN}The above configuration makes sure that, all your packages will be published as part of the same organization.
If for some reason you are working with multiple scopes in the same repository, then here is an example:
/.npmrcregistry=https://registry.npmjs.org/
@my-org:registry=http://my-custom-registry.com/my-org
//my-custom-registry.com/my-org:username=admin
//my-custom-registry.com/my-org:_password=${BASE64_PASSWORD}
@my-org1:registry=http://my-custom-registry.com/my-org1
//my-custom-registry.com/my-org1:_authToken=${REGISTRY_AUTH_TOKEN}
email=<test@user.com>
Refer the below resources for more information:
Commit
As commitizen is configured to form conventional commits, use the following to make any commits:
npm run commit