Adventures in creating a React component library with Create React App and TypeScript

Stokedbits
8 min readNov 25, 2017

--

I’ve recently been working on a project that has the main objective of being a React component library. There are tons of great articles of how to do this by ejecting a Create React App (CRA) project and customizing it a bit to do whatever one may want. However, I couldn’t quite seem to find anything that would match the criteria of what I felt was needed to meet my objectives.

The component library that I’m working on is to be consumed by another CRA project. Unlike the base CRA setup, it also needed to support TypeScript, SCSS, Bootstrap 4 as a baseline, and include a reference site to demonstrate the components. Again, this wouldn’t be that crazy to accomplish if one were to use the eject functionality of CRA and tweak all that functionality into place. In this case, it seemed like a better approach to not eject the app and see how easy it would be to roll in the desired functionality, while also retaining the ease of use of CRA.

After scouring the internet trying to find any sorta article that might have guidance on how to accomplish this objective, I couldn’t find anything that really pieced it all together. So, I’m hoping by writing this all out I can help others and get some constructive feedback to perfect the approach in which will be demonstrated.

This will be written with the assumption that the developer utilizing these instructions has basic working knowledge of the NodeJS ecosystem and experience in front end development with React and CRA. Although that being said, everything here should be pretty easy to follow and if you have any questions just hit me up 🤓.

Setup

First off let’s start a new CRA project, I’ll call mine cra-lib. In this example Yarn will be removed and we will be using NPM. I’m not advocating either package manager, use whatever you ❤️.

// Install CRA globally
npm install -g create-react-app
// Create a new CRA with the TypeScript React scripts
create-react-app cra-lib --scripts-version=react-scripts-ts
// Change directory into the new project
cd cra-lib
// Remove node modules installed with yarn
rm -rf node_modules/
// Remove yarn lock
rm yarn.lock
// reinstall all of the packages with NPM
npm install

🛑 What the heck is the TypeScript React Scripts

💡 Good ole Microsoft created a project called TypeScript-React-Starter repository to help anyone who wants to use CRA with TypeScript.

Testing

Alright back to the magic. So now we have to make sure tests are working properly. In order to make this all work with the latest version of React (V16), we need make some configurations in our test environment.

/* 
Install enzyme, the enzyme adapter for react 16, and the types for both enzyme and the adapter
*/
npm install --save-dev enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme-adapter-react-16

🛑 What the heck is Enzyme?

💡 Enzyme is a popular JavaScript Testing utility made for react

Now we have to create a file called setupTests.ts in our src directory. Once created add the following to the file:

// Temporary hack to suppress error
// https://github.com/facebookincubator/create-react-app/issues/3199
// tslint:disable-next-line:typedef
window.requestAnimationFrame = function (callback) {
setTimeout(callback, 0);
return 0;
};
import * as Enzyme from 'enzyme';
import * as Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

🛑 What the heck does all this stuff in setupTests.ts do?

💡It is a global setup file that initializes our test environment. This one specifically configures enzyme to use the React 16 test adapter and takes care of the referenced bug.

Now let’s install the jest-cli which is Facebooks own testing solution for JavaScript and fully supports React.

npm install jest-cli --save

After Jest has been installed let’s update our App.test.tsx to use enzyme.

Styles

As shown in the CRA docs it’s pretty easy to add Sass. We’re gonna take the great documentation here and tweak it a bit to work with our setup.

// Install Sass CLI /w node-sass-chokidar - A thin wrapper around node-sass executable to use chokidar instead of Gaze when watching files. https://github.com/michaelwayman/node-sass-chokidar
npm install --save-dev node-sass-chokidar
// Install across platform CLI tool to run multiple npm-scripts in parallel or sequential. https://github.com/mysticatea/npm-run-all
npm install --save-dev npm-run-all
// Installs a cli package that helps with copying files
npm install cpx --save-dev

Now that we have some of the Sass dependencies taking care of let’s setup our NPM scripts in our package.json to look like the following:

Now let’s change the extension on our CSS files to SCSS, as the CSS should now be generated by our NPM scripts.

At this point let’s start the app to make sure everything is legit.

npm start

A browser should launch by default and if everything is working right the standard CRA site should greet you.

As shown in the CRA docs it’s also pretty easy to add Bootstrap to a project. We however are going to use Bootstrap 4 and Reactstrap instead of the what is documented in the referenced CRA docs. Feel free to follow the documentation on the Reactstrap site as they are keeping it up to date as they continue to sync up with Bootstrap as it finalizes version 4.

For this part of the write up we just need to install Bootstrap via node. Again this version is going to constantly change so please check the above sites for the latest versions and implementations.

npm install bootstrap@4.0.0-beta.2 --save

It’s worth noting here that this article will not be demonstrating how to use Reactstrap. It’s purely being referenced here so that if you want to use any of the JavaScript features of Bootstrap it will be required to pull in at a later time.

Components

Now that we have our app running and building, let’s create some components. In our source folder let’s create the following structure and an example component called HelloWorld

src/
components/
HelloWorld/
HelloWorld.scss
HelloWorld.tsx
index.ts
README.md
index.scss
index.tsx

Let’s go over the content of each of these files.

index.tsx

This is the base index.tsx which will reference our index.css which is generated from our index.scss and export references to all of our components. Every component will have to be manually referenced here.

index.scss

This is the base styles file which we will use to import anything we may want in our application. As of now it just pulls in bootstrap.

HelloWorld.tsx

Nothing special here, just a basic hello world component that illustrates how to implement props and state in a TypeScript React component.

index.ts

Although this isn’t completely necessary as we could just do a more direct reference in our index.ts which is explained above, this allows us to make an easier reference by providing a default index.ts.

README.md

This piece will be for our next section. When using Styleguidist, example components will be generated by using a README.md in each of the components directories. This will just render the default component without adding any custom props.

HelloWorld example:```js<HelloWorld/>```

Styleguide

Now let’s setup our styleguide website with Styleguidist

// Install React Styleguidist
npm install --save-dev react-styleguidist

After the Styleguidist package and it’s dependencies are finished installing, let’s add the following NPM scripts to our package.json

"styleguide": "npm-run-all -p watch-sass-to-css styleguidist",
"styleguidist": "styleguidist server",
"styleguide-build": "styleguidist build",

Now let’s configure styleguidist for Typescript

Install react-docgen-typescript

npm install react-docgen-typescript --save

Create a file in the project root called

styleguidist.config.js

Now we can run the following to start our styleguide site

npm run styleguide

Distribution

Now let’s get the project ready for distribution. First let’s modify the package.json to set the private value to false. This will allow us to publish our project. Next let’s you’ll want to add the files to be packaged by adding the the files property that will point to where the contents of our library will be located after the build. After that let’s also make sure the distributed package knows where it’s entry point and types is located by specifying a main and types entry following.

Your package.json should look something like what is shown below.

Next we’re going to update the original tsconfig.json to have support to be more widely imported, generate it’s own type definitions, and a few other minor tweaks. I’d just recommend reviewing what’s going on here in TypeScripts documentation to understand everything that is going on.

tsconfig.json

Now all should we have to build, pack and publish this project via NPM assuming npm is setup properly to publish is run the following commands.

npm run build-lib
npm publish

🛑 What if I want to just test this out locally without publishing to NPM

💡 It’s easy, one way that is pretty straight forward is to just use NPM pack and tell NPM to install from the generated NPM package

// Go into your project root and run
npm pack
// I like to move the package to somewhere like my desktop so it would look something like...
mv your-package-0.1.0.tgz ~/Desktop
// Then go to your consuming project (we will make one in the next step) and run
npm install ~/Desktop/your-package-0.1.0.tgz

Consuming the library

So now that we have a basic library published, it’s time to add it to a project. Let’s just create a new CRA project.

// Create a new CRA with the TypeScript React scripts
create-react-app cra-lib-demo --scripts-version=react-scripts-ts
// Go into the newly generated apps directory
cd cra-lib-demo
// Remove node modules installed with yarn and yarn.lock
rm -rf ./node_modules
rm yarn.lock
// Install node modules with NPM
npm install

Next let’s install our new library

// Our example is called cra-lib and published to NPM so it's pretty easy
npm install cra-lib

Now let’s go into our consuming applications src folder and modify our App.tsx to display our HelloWorld component from our library.

At this point starting the application using the npm start script.

npm start

The app should launch and should be display the text “Hello World” which is exactly what our component is designed to render by default.

Summary

Our objectives are now complete. We can distribute a library to both a standard CRA project or a CRA project with the TypeScript React scripts. The library includes features such as TypeScript, SCSS, Bootstrap 4, Jest, and Enzyme. It also wraps up with how to publish and import the library into a freshly generated CRA project. There is much more we can do as far as features people may want to add or optimizations specific to distributing an NPM package that is a component library.

Thanks for reading my article and hope it was helpful 😃

--

--