Making a UI Library with React & TypeScript
In this project we're going to create the simplest UI library imaginable. It's going to be written using React and TypeScript and published as a package on NPM.
npm init
Create an empty project and run npm init
. Enter in the requested information when prompted.
Next, install any necessary dependencies. For my project, all I need is React and TypeScript.
tsconfig
Inside of the root of the repository, add a tsconfig.json
file. I always recommend de-duplicating work and that means using @tsconfig/recommended
. My tsconfig.json
file looks like this:
{
"extends": "@tsconfig/recommended/tsconfig.json"
}
Although this is a great start for tsconfig
, look at all the different options and choose the ones that you think is our best for your project. In my case, I am using React, so I'll need to set the jsx
compiler option.
jsx preserve
TypeScript ships with three jsx
modes:
- preserve
- react
- react native
I'll be using "preserve" as I'm making a library that's going to be used somewhere else and I don't want to transpile into JS like below
❌ <div/> ➡ React.createElement("div")
Making a UI Component
Next, I'm going to create src/index.ts
with a simple component (src/Button.tsx
) that doesn't do anything.
export const Button = () => null
And export that component from my index.ts
file:
export * from "./Button"
Styled Components
I need a way of styling a component, and as the name suggests, styled-components
is a library that uses SASS-like syntax to generate React components. It's something that I've enjoyed using frequently. I'm going to install styled-components
and its type library
npm i styled-components
npm i -D @types/styled-components
I then replaced my button functional component with this styled component syntax for a button
export const Button = styled.button``
This styled button component doesn't really do anything different than this code below:
export const Button = (props) => <button {...props} />
`styled-componentsa gets much more powerful when you start putting the SASS into it. In this case I'm just going to make a simple example and just make the background color green:
export const Button = styled.button`
background-color: green
`
Making a Package
Unbelievably, we've created our first UI library. A UI library isn't really something that is usable until you make a package out of it. First, open package.json
and change the main
field to look inside the src
directory:
"main": "src/index.ts",
Secondly, what needs to be packed is specified by in package.json
:
"files": ["src/**/*"],
The src/**/*
is a glob pattern that matches everything inside the srcdirectory. Next thing we do is to run the npm pack
command and this will zip everything up into an archive, which represents what will be delivered to users when they run npm install
. This is inside the generated tar zip file coradion-ui-1.0.0.tar
:
src
| Button.tsx
| index.ts
package.json
Although there are a few more things inside of our repository, the only things that are packed is the src
directory and the package.json
file. There are a few things that are always included by default like the package.json
and a readme. We don't have a readme so let's add one now in the root of the project README.md
:
# @Coradion/ui
This is a readme
When I run the npm pack
command again, inside the package there's now a readme file:
src
| Button.tsx
| index.ts
package.json
README.md
NPM Link
To test a package without publishing it to the NPM package repository use the npm link
command. Inside your package run:
npm link
Inside the project that is going to use your package run the same command, but with the name of the package:
npm link @coradion/ui
Inside our project node_modules
folder you can see we have a new folder called @coradion
and it has a bin file called ui
The package can now be imported in your project like this:
import { Button } from "@coradion/ui"
Why Are Other Packages So Complicated?
Looking at a library like Material UI and there are an overwhelming number of files and dependencies. The main package.json
alone is 227 lines long with 117 dependencies!
Even looking at tooling, they have Lerna, Rollup, TypeScript, and Webpack. It's easy to look at all that complexity and assume it's difficult to create your own package. Yet, what was created in this post is a suspiciously simple example of a functional package with a fraction of the complexity.
TL; DR
I start off with a fresh project and make a UI library (that only has one basic component) using TypeScript, ReactJS, and Styled Components. I also make a basic NPM package that can be used locally to test the UI library.