Next.js in Turborepo with Chakra UI & TailwindCSS
Hello everyone 👋, in this tutorial, we are going to build a monorepo using Turborepo and then add Next.js projects and integrate Chakra UI and TailwindCSS with toppings by setting up shared TS, eslint, husky pre-commits configurations.
Overview:
- Setup turborepo monorepo with Next.js applications.
- Setup TailwindCSS
- Setup shared typescript configuration, eslint configuration, husky pre-commits, and shared UI library
- Integrate Chakra UI and TailwindCSS in the project.
Turborepo
Monorepos that make ship happen.
Turborepo is a high-performance build system for JavaScript and TypeScript codebases. — turborepo.org
Turborepo is a blazing fast build system for JavaScript/TypeScript monorepos: codebases containing multiple projects, often using multiple frameworks, in a single unified code repository.
Why Turborepo?
- Fast incremental builds with content-aware hashing: Turborepo builds the app from the cache if the
caching hash
is not altered. This makes the build of the application fast by determining and figuring out what needs to be built. - Zero runtime overhead: Turborepo doesn’t interfere with our runtime code or touch our source maps. It only does what it needs to do.
- Parallel execution: Execute builds using every core at maximum parallelism without wasting idle CPUs.
- Other reasons to use Turborepo are enlisted in the docs here.
Let’s start by creating a Turborepo monorepo with the following command:
npx create-turbo@latest
This command will first ask us where we want to create our turborepo and then it will ask which package manager we’d like to use. (I chose yarn,
you can choose: npm
or pnpm
(Personal preference).
Once the setup is complete, we can cd
into the directory and open the project in IDE of your preference.
Let’s go ahead and talk about the folder structure and project created by turborepo.
- apps: applications we have. By default, it creates
docs
andweb
applications which are both Next.js applications. Since we’ll be creating our own Next.js applications with tailwindcss setup, we’ll later delete these two and create our own. - packages: This is the file for shared configuration (eslint, tsconfig and a shared ui for the monorepo)
- package.json: as we can see, this uses
yarn workspaces.
We have our dependencies and scripts here, which run the applications inapps
directory and run its command. - turbo.json: Turbo configuration with pipelines: build, lint, dev. This is a powerful tool. It allows us to create ,
dependsOn
: hierarchial running of commands. In the screenshot above, build pipeline depends on [‘^build’], caret means: it depends on topological dependencies to build first and then build ours. It makes sure all dependencies and dev-dependencies of a project are built first and then only runs the build script.
Now, let's remove the code that we won’t use in this project:
rm -rf node_modules apps/docs apps/web
Reason: We’ll add the Next.js application with typescript
and tailwindcss
configured. Let’s add the app by using the following command:
cd apps && yarn create next-app --example with-tailwindcss demo-app && cd ..
NOTE: In this tutorial, I’ll be showing the configuration file and setup for single project, but the process will be the same if we need no projects inside
apps
direcotry.
This will create demo-app
in our app's directory with typescript and tailwind configurations.
Now let us make slight updates to our package.json
and next.config.js files in the demo-app
project. Let’s start by adding the name,
version
attributes and then add ui
and ts-config
dependencies from the packages directory. Now your package.json
and next-config.js
file will look like this.
Now run: yarn install
Now let’s go ahead and create shared configuration files. Let's go-to packages
directory where we have our typescript and eslint configurations. As we can see in the tsconfig
folder, we’ve base.json which is extended by next.json.
As we already have the code here, why make it redundant by adding the same code in demo-app
project. So let/s change the tsconfig.json
file in the project to look like this:
Prettier Configuration
Now, let’s add prettierrc.json
file in the root directory. We don’t need to do anything as it was already installed when we ran turborepo setup. So just adding the file with configuration works like a charm. My prettierrc.json file looks like this:
Shared eslint configuration
As we’ve configured share typescript configuration, let’s go ahead and do the same for eslint
configuration as well. We can add the configuration as per our requirements. But just for the demo, I’ve added a "no-console": 2
rule in packages/eslint-config-custom/index.js.
module.exports = {
extends: ['next', 'prettier'],
rules: {
'no-console': 2,
'@next/next/no-html-link-for-pages': 'off',
'react/jsx-key': 'off',
},
};
Now create a .eslintrc.js
file in demo-app's root directory and add the following lines of code.
What we are basically doing is extending the eslint configuration
from the shared eslint-config-custom
folder. Just for verification, we can add a console.log statement in our code: we get error as in screenshot below:
Shared Tailwind Configuration
We already have tailwindcss setup in our demo-app
project.
In case we need next application, we need to share the tailwind configuration as well. Let’s go ahead and do this in this new section. Let's do this by adding dependencies we need to configure tailwind: postcss autoprefixer prettier-plugin-tailwindcss and tailwindcss.
Add dependencies to ui
package using the following command:
yarn workspace ui add -D tailwindcss postcss autoprefixer prettier-plugin-tailwindcss
What this does is, add the dependencies in the packages/ui
directory. Now, let’s go ahead and add tailwind configuration files in packages/ui
:
postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
tailwind.config.js.
module.exports = {
content: [
'./**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [],
}
Now, let’s import the shared config to tailwind.config.js
in demo-app.
module.exports = {
...require('ui/tailwind.config'),
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'../../packages/ui/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [],
};
Also, since we are using Next application: let’s update tsconfig.json
file in packages/ui
to take configuration form packages/tsconfig.
{
"extends": "tsconfig/nextjs.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
To test, that it is working, let’s modify the button component in packages/ui
as:
import * as React from 'react';export const Button = () => {
return <button className="rounded-full bg-blue-500">Boop</button>;
};
and use the component in the index.tsx
in demo-app.
We can see that our tailwindcss is working in our project. 🎉
Husky and lint-staged Setup:
Husky improves your commits and more 🐶 woof! — husky
Husky is a cool tool that allows us to manage how different hooks work with our GitHub repository so that we can add different scripts to run against different types of git hooks like pre-commit, commit-message, deploy, and so on.
lint-staged: what lint-staged does is instead of linting all files when we run lint, we could run lint-staged and it only lent the changed file so it saves us some time.
Now, let’s go ahead and add Husky and lint-staged to our project.
yarn add -D -W husky lint-staged
What this command does is, -W
installs dependencies in root package.json.
Add 'prepare':'husky install'
, script in scripts
section in root package.json
file. This runs and installs husky. Now, run to install husky.
yarn prepare
Also, it creates .husky
folder in the root of the repository. To configure lint-staged,
let’s go into our package.json
file in the root directory and add the following code:
"lint-staged": {
"apps/**/*.{js,ts,jsx,tsx}": [
"eslint --fix"
],
"packages/ui/**/*.{js,ts,jsx,tsx}": [
"eslint --fix"
],
"*.json": [
"prettier --write"
]
}
What this actually does is it lints our projects in apps
and packages/ui.
Now let’s add the pre-commit hook using following command:
npx husky add .husky/pre-commit "npx lint-staged"
This command creates a pre-commit file in .husky directory:
This runs lint-staged against the files that are staged for git before letting the user commit. Now to test and verify, that our husky and lint-staged setup is actually running, let’s try to commit the changes in our project using following command:
git commit -am 'medium article setup turborepo, tailwind, lint, husky'
And just for verification, when I add console.log("TEST")
in index.tsx
and try to run the git commit, our lint-staged throws an error:
After removing the console log the commit is successful.
Now, let’s learn about ChakraUI
and integrate into our very own application we created in this tutorial.
Chakra UI
Create accessible React apps with speed
Chakra UI is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications. — chakra-ui.com
Now, let’s start by adding the chakra UI packages to our project by using the following command:
Note: -W
flag installs the dependencies in our root package.json
file.
yarn add -W @chakra-ui/react @emotion/react @emotion/styled framer-motion
Once, the setup is complete, we can see the dependencies in our package.json file. To test the setup, follow the following steps:
- Update
_app.tsx
file in our Next.js project. Wrap it up withChakraProvider.
2. Now, let’s update our button component with Chakra Button
component.
3. Let’s use our button in the index.tsx page. Voila, we have our ChakraUI working like a charm.
Finally, we’ve our monorepo application built with Turborepo
having Next.js
application. We also setup shared typescript, eslint, tailwindcss configurations.
And lastly, we setup the husky pre-commits.
We then integrated ChakraUI
in our application.
For reference, below is the Github repository link. If anything comes up, please feel free to leave your comment. Your feedback is really appreciated. 🙇 🙏
Github repo: https://github.com/UjjwolKayastha/next-chakra-tailwind
See you in next article. 👋 Happy coding. 👨🏻💻