Getting Started with TSDX and Storybook

Yoav Ganbar
FedBites
Published in
9 min readApr 27, 2021

--

If you’ve got to this post, you probably want to start building a React components library in a hassle-free way.

I’ve stated more than once how fast it is to kick start a React / TypeScript project, and even went a step further and described how to build a design system.

But why would you want to do such a thing? What’s in it for you?

My guess is you want to share code with your team, or even with the whole interweb. Maybe, you have this set of components in your arsenal you keep writing over and over and you just want one place to “store” them and reuse them.

Wouldn’t it be great if the API for you’re components is also type-safe and (almost) self-documented?

Besides that, how awesome would it be to only focus on one task and one component at a time while you build it? Isn’t that better than just slapping in some code into some other gigantic code base?

Setting up a TypeScript project is definitely easier than it was a year or so ago, but it’s still an annoyance. Also, setting up Storybook is easier than ever, with just about one command.

But we are developers. We are lazy. We want stuff done for us, or we try to make it work for us faster next time.

This is why bootstrapping a repo with TSDX is just what you are looking for.

By the end of this article, you will be able to publish a component library to NPM with all the bells and whistles of a highly optimized and modernized stack. You will have bundles that are compatible with ejs, esm, umd, modern browsers and even NodeJS while getting tree-shaking support and all with almost zero configuration.

Are you convinced yet? Read on.

What is TSDX?

TSDX is a project by Jared Palmer which includes the best practices and optimizations for modern NPM packages baked into it. To quote the website:

“TSDX is a zero-config CLI that helps you develop, test, and publish modern TypeScript packages with ease — so you can focus on your awesome new library and not waste another afternoon on the configuration.”

In short, it takes away most of the hassle of setting up a package as stated above. Other than that, it sets up all you need to get your package ready for publishing, as well as giving you a playground for testing out your package with an example repo.

ℹ For more info on how a TSDX project is structured read my post “Building TS Packages: Anatomy Of A React StoryBook TSDX Project”

Bootstrapping a New Project

Like I’ve mentioned before, all you need to get started with it is one terminal command:

npx tsdx create my-new-project

After running this command a new folder will have been created with the name my-new-project and all the relevant packages will have been installed. Once that step has finished, a prompt will appear in the terminal asking to choose a template:

Choose react-with-storybook.

After doing so, all Storybook related packages will be installed as well:

Once it finishes, a confirmation will appear:

Now we can cd into our newly created directory and start Storybook by running:

yarn storybook

Out of the box, TSDX has created one component for us, which we can see in our local running Storybook:

This would be a good time to stop and have a look at our project's structure.

Proposed Development Workflow

After we’ve got our package and repo all configured, let’s see how we develop a new component.

We’ll start off by creating a new component, a button for example (and yes it is an overdone example, apologies I’m not more imaginative 😅).

As we’ve learned from the section above, the right place to start off is our src directory, in it we'll create a new file:

// src/Button.tsximport React, { FC } from 'react';export interface ButtonProps {
text: string;
}
export const Button: FC<ButtonProps> = ({ text }) => {
return (
<button style={{ backgroundColor: 'blueviolet', color: 'white' }}>
{text}
</button>
);
};

Now, let’s add a story for it using Storybooks recommend way to write stories using the “Component Story Format (CSF)”, so we can see what we’ve created:

// stories/Button.stories.tsximport React from 'react';
import { Meta, Story } from '@storybook/react';
import { Button, ButtonProps } from '../src/Button';
const meta: Meta = {
title: 'Button example',
component: Button,
argTypes: {
text: {
control: {
type: 'text',
},
},
},
parameters: {
controls: { expanded: true },
},
};
export default meta;const Template: Story<ButtonProps> = args => <Button {...args} />;export const Default = Template.bind({});Default.args = {
text: 'My Button',
};

Next, we’ll spin up Storybook, by running yarn storybook from the root folder of our project. And there we have our newly created component:

This looks ok. However, let’s say we wanted to update the design a bit, for example add some padding, make the font bigger, and make the button more round. We can go back to our components’ code and add some more styles:

// src/Button.tsximport React, { FC } from 'react';export interface ButtonProps {
text: string;
}
export const Button: FC<ButtonProps> = ({ text }) => {
return (
<button
style={{
backgroundColor: 'blueviolet',
color: 'white',
fontSize: '17px',
padding: '10px 16px',
borderRadius: '99999px',
}}
>
{text}
</button>
);
};

Because our Storybook dev server is running, all we need to do is save our changes, and voila! Our Storybook has been updated:

Preparing to Publish

Before we can even start thinking about publishing we’ll need to even see if our code can be used. For that, we have our example project in our directory.

Let’s open up index.tsx:

// /example.index.tsximport 'react-app-polyfill/ie11';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Thing } from '../.';
const App = () => {
return (
<div>
<Thing />
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));

We can see that the TSDX example component is imported there and rendered in the App. We can now run this by installing our dependencies (yarn install from the /example directory) and running the project (yarn start).

⚠️ Note: while trying to run this for the first time I encountered an error in which parcel (which runs the example app) did not work. The quick fix for this is changing the devDependencies for parcel and pining it to 1.12.3 (remove the ^ symbol). More info can be found here.

We should see the demo component rendering on localhost:1234:

That looks alright. To make sure we have built our latest we could have just run yarn start from the root of our project, or, for an ad-hoc purpose we can run yarn build - that will make sure our code has been built and compiled into our dist folder.

Now let’s try and import our Button in the same manner:

// /example.index.tsx// ... 
// note that we are importing from our `dist` directory
import { Button } from '../.'
const App = () => {
return (
<div>
<Thing />
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));

If you are using VScode, like me, (or any other Typescript supporting IDE) you will notice that button has a squiggly red line…:

This is no bueno… Why is this happening?

Long story short, the entry point for the project being compiled is the index.tsx file at the root of the project, and it does not have our Button in it. Even though it does output type definitions to the file, if we peek at /dist we will see a Button.d.ts.

Let’s fix this. We’ll head to our code and change our demo component in index.tsx and name it Demo.tsx then we'll change our index.tsx like so:

// src/index.tsx
export * from './Demo';
export * from './Button';

Now, let’s go back to /example/index.tsx and we will be able to see the squiggly line is gone. Success!

We’ll double-check ourselves and go to our running example app once again to see if our button is rendered:

And it’s there! 🤘🏽

Publishing to NPM

If we would want to publish our package now to NPM, how do we go about it?

Lucky for us, most of the setup for this has been done for us by TSDX. However, as we’ve not set up our project in git we need to take a few more steps:

  1. Make sure you have a Github account (🙂).
  2. Go to https://github.com/new and create a new repo. Name it and mark it as public.
  3. Go to your terminal and navigate to the folder you’ve created the project on.
  4. Run git init then git add . and finally, commit with git commit -m "initial commit"
  5. Push the existing repository from your command line by copying the instructions from your newly created Github repo. (make sure your code is there by refreshing the page)
  6. Create an NPM account (if you don’t already have one).
  7. Go to the root directory of your project.
  8. Run npm login and go through the prompts using your credentials from step 6.
  9. Run npm publish
  10. Celebrate 🥳

Before you actually publish, do make sure you set your package dependencies correctly (more info here).

At this point, you have published your package to NPM. It should be in your account under packages.

Now, to make sure it all worked as expected and that anyone could install it, we’ll turn back to our example project within our directory and install our newly published package.

For the sake of this post, I have published a package called post-tsdx-project (you can find it on npm here).

Now, we can go ahead and install it by running: yarn add post-tsdx-project (this would obviously also work with npm i post-tsdx-project, but in our case, the example project is using yarn. Worth noting that the yarn package registry mirrors npm's and can be installed from there).

Then we can change the code in our example:

// /example.index.tsximport 'react-app-polyfill/ie11';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
// we change:
// import { Button, Thing } from '../.';
// to:
import { Button, Thing } from 'post-tsdx-project';
const App = () => {
return (
<div>
<Thing />
<Button text="test" />
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));

Let’s run the app again with yarn start. And voila! it works:

Conclusion

I hope that this post has helped you understand how and why you would want to use TSDX and Storybook.

The way I see it, Storybook one of the best tools to rapidly develop UI, paired with TSDX, It’s never been easier to ship reusable UI component packages and libraries.

A quick feedback loop with a low friction set-up is what I look for in most of the tools I use as a developer.

If you’ve enjoyed reading this and have found it helpful, please share this with your peers 🙂.

--

--

Yoav Ganbar
FedBites

Father. JavaScript enthusiast. I Love web development. Developer Experience @ Builder.io. Previously: Tech Lead & SW Architect @ Fiverr, Capitolis, Culture Trip