Intro
It will be a longer article about project setup. Prepare a huge mug of coffee before you start reading.
Prelude
Developers should work on functionalities. Did I say something weird?
Many of you may disagree. What about creating a project? What about connecting eslint, prettier? What about preparing a test environment for unit and e2e tests? Seems like a lot of work. Honestly, it may be...
However, today you'll see how you can do it easier/better/faster/stronger 🎧.
Here is a final result. Don't be afraid, just use it :0.
1. Monorepo
We'll divide our solution into applications and libraries. All of these will be stored in a single repository with the same configuration for linting, formatting, TypeScript, jest, cypress, storybook, ...etc.
Thanks to this you will have the opportunity to run the applications separately or together. This is what you can achieve with monorepo. In simple words, it's a way of storing and managing a solution, which involves keeping the code in a single repository and dividing it into packages and applications accordingly.
2. Monorepo with nx
It's mainly a CLI and patterns that helps you to create and manage monorepo. Below is a list of cool things you will get with this technology:
- apps and libs generation via CLI
- organized structure
- option to configure stuff like storybook via one command
- dependency preview via graph
- same configs for everything
- configuration of the TypeScript
- option to choose the way of styling your components
- routing configuration
- bundler configuration
- unit and e2e tests configuration
- and many more...
3. What will we create?
We are going to make a solution that will allow you to manage your blog and display the created articles/courses. So let's describe the components we'll create:
- application for presenting the blog content and homepage
- a blog management application
- library for communicating with the API
- utilities library
- a library with components
Each component will be independent. It will reduce the risk of conflicts. Libs will be consumed by apps.
Loading
Building blocks
Check out this article if you are interested in architecture.
4. Initialization of the project
First, install this script, which will start the nx repository:
npm i -g create-nx-workspace@latest --legacy-peer-deps
Later you need to type:
npx create-nx-workspace@latest
The CLI will ask you the following questions sequentially. You can find description of the options in nx documentation. Let's focus on creating our dream project.
Answer the questions the same way I did.
Loading
Questions and answers
The file and folder structure generated by nx should look like this:
Loading
Generated content for blog application
If you look at it you will see that by default you have TypeScript, prettier and eslint configured. In addition, tests in jest have been configured and e2e in cypress.
We already have a project and app in Next.js. Now it's time for the single page application in React.
cd .\system\
npx nx g @nrwl/react:application blog-creator
The tool may ask you to install additional dependencies, which it uses to generate folders and files. Confirm it. After that, you will see similar questions as before. Answer them the same way I did:
Loading
React app done
We have the apps! After a while you'll see two new folders in the apps directory.
In this commit you have the full changes.
5. Creating libraries
The idea is the same, only the questions and your answers differ. It's beautiful ❤️. Type the command:
npx nx g @nrwl/react:lib figa-ui
I named my components library after my beloved cat - Figa. You can name it after a turtle (or whatever pet you have at home). These are my answers:
Loading
You have figa-ui installed :D
Now it's time to do the two simplest things. We need to create two TypeScript libraries. Type the following two commands:
npx nx generate @nrwl/js:lib utils
npx nx generate @nrwl/js:lib blog-api
In both cases, choose the same answers:
Loading
TypeScript libs are cool
We have what we wanted, so let's move on.
In this commit you have the full changes.
6. Adding storybook to UI library
Storybook will be needed for our components library. Nx allows per scope configuration or global one. We need scoped. So let's enter some commands:
npm i --save @nrwl/storybook --legacy-peer-deps
npx nx g @nrwl/storybook:configuration figa-ui --uiFramework=@storybook/react
As usual there will be questions, answer as I did:
Loading
Storybook ready!
Now you can start the storybook process and play with it via this command:
npx nx run figa-ui:storybook
After a while, the storybook window will launch with the message that you don't have any story yet. We'll add the Bell.tsx and Bell.stories.tsx component to see if everything works as it should.
Loading
It works!
In this commit you have the full changes.
7. Running the applications
Now it begins :D. You can run all applications, one of them, or just the storybook. It depends what kind of feature you are already working on. To run single app type:
npx nx serve blog
You can run all apps via:
npx nx run-many --parallel --target=serve --projects=blog,blog-creator
or
npx nx run-many --target=serve --all
You can get an error about same port usage attempt. To fix it you need to visit project.json files and add port number. For each application add different one. After that type:
npx nx run-many --target=serve --all
and all applications will be run on different ports, in my case 3001 and 3002. Looks cool? Yea!
In this commit you have the full changes.
8. Using libraries
Take a look at this. Do you want to use a components library in two applications? Forget about npm install. All you need is a simple import statement.
Loading
Who cares about configuring aliases for webpack? Certainly not you :D
In this commit you have the full changes.
9. Adding custom fonts
We need to download the fonts. Lexend variant will be suitable for us so let's take it from Google Fonts in the following sizes (300, 400, 500, 700). Immediately after that, we need to place the files in the appropriate directory - our figa-ui library.
We are using styled-components so there is a need to create global styles, in which we'll declare our setup for fonts.
Loading
The current setup doesn't work yet. We need to add minor changes in the preview.js file and its extension must be changed to .tsx.
Loading
In the ts.config file, we need to add preview.tsx to include array. Errors will be displayed if we skip that.
Loading
Well, how to test it? We need a component. Below is a partial implementation of Font component and usage in various places.
Loading
At this stage, the fonts should look the same in the storybook, in the SPA application in React, and in the Next.js application.
In this commit you have the full changes.
10. Adding husky
Husky is a library that allows you to run a scripts before a specific git hook. It is usually used to check code consistency, run tests and format code before commit. Let's install it via command npm i --save-dev husky.
On this page you have a list of all hooks.
Before each commit we need to format code with prettier and perform check with eslint. Fortunately for us, nx provides the configuration of both by default so the only thing left is the husky setup. To do this, we need to define an aliases for the scripts in package.json.
Loading
Now every developer must run the prepare command after downloading the repository. Without that, husky will not work. After launching, you should see the .husky directory and the pre-commit file that needs to be changed.
Loading
Husky ready
The last change is to define the scripts that will be executed before the commit.
Loading
Now every time when someone tries to do a commit then the scripts will be executed. If you want to skip the husky because you're just checking something, then use the git commit -m "I'm just checking" --no-verify.
In this commit you have the full changes.
11. Adding themes
We want to give users the ability to set everything in the application according to their preferences. This would be useful for creating predefined themes or to debug interface behavior by developers/designers.
It may seem like overkill, but admit that it's a pretty cool feature and not difficult to implement. You just have to stick to the specific guidelines that we're about to establish. Let's start by defining the design tokens and the shape of theme.
Design tokens? These are nothing more than rigidly fixed values that our design system will use. It can be background color, font color, border rounding, font size and others... To simplify, these are nothing more than unique variables to which we assign the settings of our design system.
- background.color='#f0f0f0'
- font.color='#000000'
- boxes.radius100='4px'
- font.size100='16px'
Loading
Note the quite "strange" shape of the tokens object. This manner is taken from Material design to which I refer you in this link.
That themes should allow us to describe each component in our UI library, so we need to make sure that the values are taken from the theme, which the user is using.
Loading
When you add a new component, remember that all settings should be added to the themes, and if you use a new font size, color or anything else, you need to include it in the design tokens. Thanks to this you will get a very scalable components library. Now it's time for real work. Let's use Context API to provide logic for themes management.
Loading
Done! All that's left is to use the theme in the styles and you can enjoy the ability to set up the UI in any way you want. Now with every next component you just need to use this approach.
Loading
Let's assume that you want to use a component, and later you want to change theme. All you need to do is to wrap the whole application with the provider and import the dedicated component.
Loading
Loading
Themes showcase
In this commit you have the full changes.
12. Sharing fonts between libs and apps
If you run the application or storybook and you don't have "Lexend" fonts installed, then get ready for the error in the browser console. We added the font face and font files, but did we think to move them to the public directories for the application or indicate for the storybook where these fonts are? It seems that no...
The case for the storybook is easy. Just indicate the place from where the storybook should copy our assets and it will be done automatically during build. I moved the font files to the very top of the file and folder structure, just for convenience when importing. Next, I changed the configuration in the main.js file.
Loading
I corrected the import in font face to be shorter. The assets directory is redundant. Immediately after that, the fonts in the storybook updated their look and started to look like a real "Lexend".
Loading
Now the applications. Here is a little more work. We need to use the webpack plugin to copy the contents of one folder to another. We'll do this for both applications in Next.js and in pure React.
Loading
Loading
In this commit you have the full changes.
13. Fixing a strange error in styled-components
If you launch the console in the Next.js application, you will see an error indicating that the generated values for the className property, are different on the server and on the client. To fix it we need to add this to next.config.js.
Loading
In this commit you have the full changes.
Full example
Repository to play with.
Summary
We went through the wonderful tool together. I recommend playing with it. We have described only a part of the possibilities.
There is still place to add many more things. However, I think that it is better to add a little less than more. The repository we have implemented will be constantly improved so we recommend to taking a look at our Github from time to time.
If you enjoyed it, be sure to visit us on Linkedin where we regularly upload content from programming.
Comments
Add your honest opinion about this article and help us improve the content.