Building A Design System Package With Storybook, TypeScript And React In 15 Minutes
When building out a UI Component Library for my own work, I ran into a couple of pain points when searching how to create a simple workflow that "just works".
This tutorial will show how to create a simple project set up a
create-react-app project to create components with TypeScript + Storybook, then finally it will demo that same design system being used as a package in another React project.
UI Component Library vs Design System
While these can be minor semantics to me, there are some important differences on what constitutes a component library vs design system that you may want to delve into and understand (unless you are happy to be corrected all the time like myself).
Essentially, we will create a simple, single-component UI Component library as the main demonstration, but the installation of Storybook will enable us to markdown guides to write principles, patterns, tone, etc.
Your willingness to update those guides are (for all intents and purposes) what will take this project from a simple component library to being able to communicate other facets that make up the definition of a "design system" as linked above.
We will begin by using
create-react-app to set up a React project and then adding in Storybook to help with component development.
As we are just going to use TypeScript for the build, I am using
copyfiles to help us copy over filetypes that are not handled by the TypeScript compiler (think CSS files, image assets, etc.). This helps us keep our build tooling as simple as can be.
We will use
rimraf as a simple pre-build clean to remove our output folder (which will be
Updating our package.json configuration
We are going to add some meta information to our package.json file. In my case, I am going to create a private npm package hosted on GitHub.
You can see an older post on creating GitHub packages, otherwise, publish however you wish to!
I have also updated the build script to build out a configuration from
We want to update the config to emit files and emit declaration files.
Here we have updated the file to remove
noEmit and then using
declaration to emit out declaration files.
This will ensure any custom types we create will make be emitted during the build for other libraries to use that support TypeScript.
Our updated package.json file contains the changes to the "build" script which uses the TypeScript compiler instead of
Our first component via Storybook
npx sb init will have created a
src/stories folder for us for a few files.
Let's move the
src/stories/Button.*.* files to
Once that is done, we will barrel the component out. We do this by creating
src/components/index.ts add the following:
Then to export this from our library, we will create a file
src/main.ts and add the following:
Our lines in the package.json file for the types, main and module keys will be pointing the created files.
Workflow with Storybook
src/components/Button/Button.stories.mdx and add some basic markdown:
Although a super basic example, this will create some interactive markdown that we can use for documentation on usage and add in our "design system" guides and principles.
Let's now run
yarn storybook to start up the Storybook environment.
Note: You'll also need to adjust
src/stories/Header.tsx to fix where Button is imported from.
Once Storybook is up and running, you can head to the localhost port to see a
Usage section with our notes on using the Button (although styled badly and bland) and you can see under
Examples/Button some examples and interactive ways to work with the component itself.
Storybook Button - Usage
Storybook Button - Story
Amazing! We have a work environment to develop our components with.
At this point, I will leave the Storybook part there as this is more of a conceptual proof and move onto publishing the library. There is a lot to Storybook, but check the docs from here on out to build further than the simple set up we have done above.
Publishing the library
Assuming that we are happy with our
Button and this will make up the sole component of our incredibly versatile component library, so let's move onto publishing the package!
For myself, I am going to run the following:
This will build the package into the
lib folder and then publish to GitHub for me. Much of this configuration and "why" is from the set up we did earlier in
Note: If you are also publishing to GitHub, ensure that you have the repo created on remote. For me, I used the GitHub CLI by running
gh repo create okeeffed/ds-pkg-blog-post --public -y and then pushing to remote. You will also need to ensure you have setup
.npmrc up correctly (either in the local project or globally). Please see my above link on creating a GitHub package to do so if you are unfamiliar with publishing packages.
Trying out the new library
Now that our package has been published, let's start up another
create-react-app project and install our package:
src/App.tsx to use our new
Running our app using
yarn start in this project and heading to localhost will show us our component in use! Clicking the button will also log out our "click" to see it all in operation.
Button from design system
What is amazing is that we also still get our type safety showing, as demonstrated when we don't provide a label:
Button without label prop
We also get our type suggestions coming through for things such as our props (as seen here with VSCode):
Button type suggestions
Success! We have gone start to finish with our design system setup and having it work as a type-safe package in another project.
Today's post is not perfect by all means and it is not all-encompassing, but I was very surprised by how few posts there were to get this all done without running into issues.
Having a basic, TypeScript-compiled solution has been working great for me as a quick start to getting things working together without too many discrepancies or issues along the way.
We have gone from a simple
create-react-app command to having a TypeScript design system that supports Storybook for both development and documentation, then we pushed and published it as an npm package hosted on GitHub and finally seen it in action in another project - all doable within the space of minutes.
Resources and further reading
Image credit: Nils Johan Gabrielsen
- aws cdk
- aws lambda
Deploy a simple Python lambda function with the TypeScript AWS CDK to LocalStack.
Deploy a Kubernetes Fargate on EKS cluster using the AWS CDK.
Use the ECS patterns library to deploy a simple Fargate cluster to LocalStack.
- aws cdk
Use the TypeScript AWS CDK to deploy an S3 Bucket fronted by a CloudFront CDN and Route53 record.
1,200+ PEOPLE ALREADY JOINED ❤️️
Get fresh posts + news direct to your inbox.
No spam. We only send you relevant content.