In this chapter we're going to learn how to set up our project. This will include learning how to install PNPM version 10, how to set up TurboRepo using ShadCNUI, Next15, Tailwind version 4 and React 19. We're then going to learn how to use the Monorepo by adding the second application and learning how to add a simple internal package. And then we're going to show a more real-world example of adding a new ShadCN component that can be shared across those two applications. And at the end we're going to wrap it up by creating a github repository to keep track of all of our changes.
In case you are confused by some of these terms I've created a little box here to help you out. PNPM is a package manager similar to Yarn, NPM or BUN. TurboRepo is our monorepo build system and ShadCNUI is our component library in the simplest terms possible. So Let's start by installing PNPM version 10. So why version 10?
Simply because at the time of me making the video that is the latest version and the version I have built this entire project with. So you can use the link you can see on the screen or pnpm.io and visit their page. After that go inside of the documentation, installation. Depending on the OS you use, go ahead and follow the Windows manual, PowerShell manual, curl, anything you want. If none of this fit your needs, you can also use NPM or Homebrew, as well as many many other options.
So after you've done that, go ahead and do PNPM version. Now what's important here is that you use PNPM version 10. I don't think it's strictly necessary to use the exact same minor versions as me, but there is a way to do that and I will show you how. But just before we do that, Confirm that you have PNPM installed, whatever version, it doesn't matter right now, I'm going to teach you how to change the version. Scroll a bit down here to find compatibility.
So since we're using PNPM 10, the node version, minimum node version is node 18. So go ahead and run this as well. As you can see, mine is 22. So I fall into this category right here, which means I am okay. PNPM 10, Node.js 22.
If you get a version lower than 18, or if you get an error, please upgrade or install node to the latest long-term support. And now about this exact version. So if you are watching this video far from now, I don't know a couple of years from now, it is possible that a new version has come out. So what you can use is self-update command and in here you can specify the exact PNPM version and then you can use the exact same as me if that's something that you want. But again if you're watching this video you know pretty early from when I released it as long as you're using pnpn10 I think that's perfectly fine.
So after you have confirmed those two things let's go ahead and let's install our TurboRepository template. The way we're going to do that is by using a ShadCN UI template. So ShadCN is our component library and what you have to do is visit ui.shadcn.com or use the link you can see on the screen and go inside of the documentation. After that go inside of Monorepo right here and you can see that even they admit that until now using chat CNUI in a Monorepo was a bit of a pain. So because of that they have created a command line interface to help you out.
And now we're going to run this to create a new monorepo project. And now I have to warn you again, use pnpm. If you try developing this project with npm, yarn, or bun, I cannot guarantee that you will be able to complete it. So pnpm has a very specific workspaces way of separating packages. I have no idea if there's anything similar in pnpm, my apologies, in npm, yarn, or bun.
In fact, when I try running this command, I get an error. So it only works with pnpm for me. So please, if you want to complete this tutorial, use PNPM. Now in order to make this even easier for you I won't be using the canary here. Canary however you pronounce it.
I will be using the exact version. So how can you check the version like this? So why am I using the exact version instead of just a tag? Well the reason is so you know what I'm using, right? Because I don't know if you're watching this six months from now or five years from now.
This version right here is basically, will basically determine your Next.js version, your React version and your Tailwind version, as well as all other internal packages. So that's why it would be a good idea to use the same version as me if possible. So now let's go ahead and run init here and let's set up our Turbo Repo structure. So make sure you select Next.js Monorepo here. And I'm going to call this app echo and I'm going to call it tutorial and after that let's go ahead and sit down relax and wait for this to install and after this was completed you will see success message project initialization completed you may now add components and now we immediately go ahead and do cd and go inside of this package right here inside of here you should be able to see the apps, the package.json, packages, pnpm lock file, pnpm workspace file, tsconfig and turbo.
Now let's go ahead and set up our IDE. I'm going to be using visual studio code so I'm going to go ahead and click open here and I will select echo tutorial. If you get prompted with this you can press yes. So you can see that the structure is a little bit different than what we are used to in normal Next.js applications. We have this weird apps folder that has the web folder and then inside of here we finally have that structure that we are used to the app folder with the layout and the page but we also have another thing called packages here And in here we have the SLint config, the TypeScript config, but also the UI.
And inside of this UI you will recognize that these are chat-cn packages. This is the popular CN package from chat-cn-ui. And in here we have the globals.css that you usually find in the root of your Next.js application. So you can see that there is definitely a little bit of a difference in this application right here. So what I want to do now is I want to make sure that you have Turbo installed globally.
So head to turborepo.com or use the link on the screen here and in the docs in the installation here you can skip this part because we are not building Turbo from scratch, but in here, you can find a guide to install Turbo globally, which brings flexibility and speed to your local workflows. So go ahead and use the PMPM selection to add Turbo globally. And once you've done that, once you run that command, you should be able to type Turbo in your project like this. And after that, go inside of your project here and run TurboDev and you will see that we have a bit of a familiar interface here but something new here. So here on the left side we have tasks.
Basically when you run TurboDev inside of your root folder in the Turbo repo, it will find all apps that need to be run as well as all packages that need to be run. At the moment the only thing we need running is the web application. So when you visit localhost 3000 you will see good old hello world with a button. Now, let's go ahead and look at our versions. So since I used ShadCN 2.9.2, my next-to-last version is 15.2.3.
The only reason I'm telling you this is so you are aware of what version I'm working with so you don't have any huge changes. And you can also see that the package.json works a little bit differently in TurboRepo here. So there is a global package.json in which we have these dev dependencies which are the shared slend config, typescript config and then some specific packages used for the entire application. You can also see that we have a very specific package manager version set here to be pnpm at 10.4.1. So this is set by shatcn template.
So what does that mean for us since we are using a higher version than 10.4.1? Well, it doesn't mean much because our higher version is backwards compatible with this version. So it would be a problem if this version was higher than our version but since it is not, I don't think it's any problem whatsoever. Other than that, you can go inside of your app, web, app, page.tsx and you can say, hello apps web. Save the file and it will change here immediately.
So now what I want to do is something fun. Let's shut down this app. And let's go ahead and do the following. So let me just see what I've changed. I've changed this.
Perfect. Let me close all of this. I'm going to go inside of apps here. I'm going to copy web and I'm going to paste it and I'm going to rename this widget. The reason I'm calling this widget is because we will have another application called widget.
So why not name it immediately? Now what I want to do is go inside of widget and go inside of package.json and just change the name here to be widget as well. I believe that everything else can stay the same and the dependencies, everything else can literally stay the same. There's nothing here that we have to modify. These two are both going to be independent Next.js applications which share the same packages from ShadCNUI as well as the same SLint and TypeScript config.
So what I suggest you do when you do this is just run PNPM install just in case so because we just added a whole new application so I want to make sure that everything is up to date. You can see that mine says already up to date but just in case you know it's always good to do that. And when you're running pnpm install make sure you are in the root of your project. So what happens now? Let's do turbo dev and now you can see something different.
You can see that our widget is now running but if you use arrow keys up and down you can change between these two applications and you can see that now one of them is running on port 3001 because obviously port 3000 is taken. Now I don't know which one of yours will run on what port, it's honestly just dependent on whichever one compiles first, right? But I will show you how to put fixed port numbers so you don't have to guess which one will be which. But what's cool now is that if you go to localhost 3000, let me just refresh this, it says apps web. So let's do this.
Let's go instead of apps, let's go instead of widget app page and change this to hello apps widget. And you can see that if I go on let me just highlight the widget dev so it's running on 3000 I am on 3000 now I'm gonna try and enable developer mode so you can see the URL here. I'm on 3000 so it says hello apps widget but if I change my port to 3001 it says hello apps web. Now I'm not exactly sure why one is in light mode and one is in dark mode it doesn't matter because we're going to remove that theme provider altogether. But you can see the point in Monorepo.
We are building two applications at the same time while keeping them separate. They are two Next.js applications in the same repository, but they are separate. And they're run separately and they will be deployed separately. And this is the exact structure that we are going to need for our project. So now we don't really have to do much anymore.
In fact, we can shut down our app. And what I want to do is I want to do the following. I want to create an internal shared package. The reason I want to do that is so that you understand what this is, because it's quite easy to understand what apps is. We've just run them and we've just modified them.
Pretty easy to understand, right? But these packages are a bit odd. You know, what are these files? You know, what is this exactly? What is this?
I mean, we know it's chat-cnui, but how exactly does it work here? I personally managed to understand this a little bit better by going inside of getting started, my apologies, inside of crafting your repository and creating an internal package. So let's go ahead and learn how to do that. So first let's create an empty directory inside of our packages called math. So I'm going to go inside of here and I will create a new folder called math.
Now inside of here I'm going to add a package.json specifically for pnpm so always make sure pnpm is installed. And I'm just going to copy the entire thing from here. So inside of here I will create package.json and I will paste it right here. Now I'm just going to change this to not use a repo and to instead use workspace. And I'm going to do the same thing here.
So the reason we are using Workspace is because the chatCNUI template is set up to use that. If you go inside of your packages, inside of UI, and click on any package JSON, you can see that this is how we call them. We alias them on the workspace. In their example, they alias them on the repo, however you pronounce it, right? But you can see that we pretty consistently use workspace.
So it makes no sense to just change that randomly. So let's go ahead and learn how to add this using their example, but just slightly modified for our example. So what I did to remind you was I changed these two instances of repo to workspace and nothing should really change right. I see no errors, nothing wrong. And just to let you know, so this workspace tags right here these are very specific to PNPM.
You can see that if you click on yarn or NPM, it's different, right? So that's why I told you, please use PNPM. I'm pretty sure that some of you are more familiar with monorepos than I am and you might think to yourself oh that's not true you can actually use yarn sure feel free to use it right I'm just trying to make this easier for everyone to follow because I know it works using this package manager so that's the only reason I'm doing so great and in here we can break down package.json piece by piece So the scripts inside of the script we have the developer script and we have a build script and basically what's happening is that both of them compile the package using the TypeScript compiler which is this tsc command. The dev script will watch for changes to the source code and automatically recompile the package. Basically hot reload is the same thing as running next dev to get your app running.
In the dev dependencies you can see that we have the TypeScript individually set but we also have a TypeScript config shared with our entire application. So why can we do that? The reason we can do that is because we have TypeScript config already in our packages. So what we've done is we've abstracted this TypeScript config and we are sharing it across the math package, across the web app, and across the widget app. So all of our individual modules inside of this monorepo are sharing the same TypeScript config but you still need to install the actual TypeScript package in the app or the package that you're going to use, right?
So this is a little bit more complicated but it gets quite you know logical as you move on and I believe this is what they explain here so TypeScript and repo TypeScript config in our case workspace TypeScript config are developer dependencies so you can use those packages in the, in our case, Workspace math package. In a real-world package you will likely have more dev dependencies and dependencies but we can keep it simple for now. Now in the exports, in here we define multiple entry points for the package so it can be used in other packages. So whenever you define a package you also need to define the exports and once you define the exports we can then import those packages inside of web and inside of widget for example. So if I'm correct we should be able to visit our packages UI and if I go inside of package.json let me see if I can find the exports I can.
So you can see the exports here. Globals.css, PostCSS.config, Lib and everything inside of it, Components and everything inside of it, and Hooks and everything inside of it. So this allows us to reference this internal package and use all of these things across other packages or apps. So be mindful of that whenever you're creating internal packages. You need to export whatever you wish to be able to import later on.
Perfect. Now let's go ahead and let's add tsconfig.json. So I'm going to copy this instead of math, tsconfig.json Let's paste this here and change this to workspace. And when you command and click on this, you should be able to open it, right? So I think that if I just leave this as a repo, you can see that I cannot open it, right?
That's because mine are referenced under workspace. So that's why we are doing this. If you have it like that, everything should be just fine. So we've done four important things here. We have created a shared...
We didn't create it here, but we are using the shared base JSON inside of our tsconfig. And then we have created the out directory to tell the compiler where to compile and the root dir to ensure that the output in the outdir uses the same structure as the source directory. So this specific thing isn't going to be too important for us. It is more important for this exact package, but still, it's a good practice to do this. And now let's go ahead and go inside of the math folder and let's create a source file.
And let me just close this error and inside create add.ts. I'm just going to copy this very simple addition function here. And then what we're going to do is we're going to go inside of one of our apps here. And let me just check if I have just a second, I want to make sure that I didn't miss a step here where we are required to run pnpm install. Okay this is what I'm going to do.
We just added the source add.ts and now let's go inside of vet package.json here and let's add add workspace forward slash math workspace colon asterix like this. There we go. Exactly like this. And in here we have a warning. You just changed the dependencies in your repo.
Make sure to run your package manager's installation command to update your log file. So let's do that. Make sure you're in the root of your app here and do pnpm install. And you can see that it actually added one package and I believe that this package is let me try and find it here you can see it basically created a proper link to workspace math right so whenever you add a new dependency inside of a package json I think specifically where is my apps package.json? Here it is.
You have to run pnpm install and once you have added that I believe you can already use it. If we go instead of apps, web, app, page, let me try and import add from workspace math add and in here what I'm going to do is just add two and two together and now let's do turbo dev here no errors that's great and you can now see that we have a third task running our workspace math dev so the reason this task appeared but our other packages are not appearing here because you've probably noticed that right this isn't the only package we have but for some reason math is running whereas UI isn't running and TypeScript isn't running and SLint isn't running. The reason for that is we added the dev script. So it noticed this script here and decided to run it. Right, So that's how Turbo decides what to run in these tasks here.
So let me see where my web is running. So make sure web is highlighted and click on that link. It will either be localhost 3000 or 3001. And there we go, 2 plus 2 is 4. And that's how you create an internal package.
I believe this is the exact thing that we just did. We just used workspace here And in here it also tells you to edit Turbo JSON. So let's go ahead and do that. Turbo JSON is located in the root of your application here. Now let me just check this.
So tasks build outputs. Tasks build outputs. Let's go ahead and let's add this and then everything inside of this. And now let's do turbo build to see what happens. Now keep in mind that there is a chance our turbo build fails for a very simple reason that they are using a different template and setup than ours, right?
So I will follow this tutorial to the end, but just because it fails doesn't mean that we did anything incorrectly it just might mean that in the next chapter we're going to explore why the build fails let's go ahead and do turbo build and you can see that now it's going to build three apps workspace, math and then web and then widget so it's building three things because it detected three build scripts across those packages and apps and you can see that three of them are successful. So basically what happened is that in each of these packages here it found the build script and run it and in our apps it found the build script and run it and in our apps it found the build script and run it and same thing for the widget. So that's how Turbo Repo works right and now the cool thing is you can go inside of your widget, go inside of package.json and you can add it here as well. So workspace forward slash math workspace like this. And here's here's a thing I don't know.
For example, I'm not really sure of the difference between an asterisk and this caret. That's one thing that you can write in the comments if you know the the answer to or research yourself. So I'm also exploring this Turbo Repo as we go along. And the reason I'm saying this is because I don't want you to be discouraged if this is new technology for you. It is also new technology for me and I learn best by building inside of it.
And I hope that you will too. So once I've added this dev, oops, should I add it in dev dependencies or Dependencies? Yes, I should add it in Dependencies. My apologies. So I'm doing that here now in the widget.
The same exact thing I just did in the web. Right? And remember, once I do that and save this file, I have to do pnpm install. And there we go. Everything's up to date, but you know, just in case.
And now let's do TurboDev again. And actually here's what we can do. Open the package.json of the widget. And open the package.json of web. And now what we're going to do is we're going to change the port of each of them.
So in the dev script, when you run next dev turbo pack, also add //port and for the web set it to be 3000. For the widget set it to be 3001. So this way whenever you run TurboDev you don't have to guess which one will be which. You can see that this one is strictly on 3001 and the web is strictly on 3000. So now I'm going to open the widget one here, which shouldn't have any calculation here.
But if I go inside of widget app page I should be able to do the same thing there we go add from workspace math add 2 and 2 together and there we go 4 here as well perfect so we now learned how to add an internal package so what I want to do next is I want to learn how to add a shatcn component but I just want to finish reading this here so best practices for internal packages one purpose per package when you're creating an internal package it's recommended to create packages that have a single purpose it isn't a strict science or rule but it is a best practice depending on the repository It's easier to understand and it's reducing dependencies per package. Here are some examples of what that means. Repo, you can read this as workspace in our case. UI, a package containing all of your shared UI components. Exactly what we have.
Tool specific config. A package for managing configuration of a specific tool. We have this exact thing with slint and tsconfig. And here's another one example. Perfect.
And as always, there are exceptions to the rule, but for this tutorial, this is just fine. So I hope this kind of cleared up how internal packages work inside of Turbo Rebo. And just to wrap this up before we start a new GitHub repository, what I want to do is add a new component to the project. So what you have to do is you have to go into one of your projects. So go inside of CD, Apps, and let's go inside of Web.
And let's go ahead and use pnpm dlx chatcn 2.9.2 and I'm going to add and let's for example add input I think that's one we don't have and you can see that it added it to packages.ui.source.components.input.tsx so you can see what it says here the CLI will figure out what type of component you're adding and install the correct files to the correct path for example if you run npx chat-cn-add-button the CLI will install the button component under packages UI and it will update the import path for components in app web. But if you add a chat-cn-block, then it will add the components under packages ui but the block under the exact app you have added and now what we can do is basically this thing which we've already done So I just added input and now I can go to for example, widget and I can extend the input here now. Oops, my apologies that's not how you use chat.cn. Input from components input and let's go ahead and add an input here. Let's go ahead and do turbo dev.
Oh yes, make sure that you exit to the root of your application and then run TurboDev. Because if you are inside of a specific directory, it will only run that directory. So here it is my widget is a 3001 and I should now see a little input here. So it works. That's how you add shared chatcnui packages across two different applications.
Perfect! This is exactly what I wanted us to achieve in this tutorial, I mean in this chapter. We got familiar with Turbo Repo and we also learned, you know, why do we need PMPM, how to change PMPM versions, how to set up TurboRepo with ShatCN templates, and we learned how to use Monorepo by adding a second app and creating one internal shared math package as well as a more realistic example of adding a shared chat-CN component. Excellent! So now it's time to wrap up this chapter by creating a GitHub repository.
So head to GitHub and go ahead and click on create new, new repository. I'm going to call this echo tutorial. I'm going to set this to private and I will click create repository. And now what we have to do is we have to use the second option, push an existing repository from the command line. But just before we do that, we have to resolve these 22 pending changes here.
So what I'm going to do is I'm going to go inside of my source control here and under my changes, I'm going to click plus, Stage all changes. And then in here, I'm gonna add feet, add TurboRepo. And I'm going to add commit. And what happened now is that we have locally saved and staged and committed these changes, but we didn't publish this anywhere. So if you lose access to this computer, this also disappears.
So what we're going to do now is we're going to copy these three lines. Make sure you have done this first and you have no outstanding changes. Go inside of your terminal. You can shut down the app. Make sure you're in the root of your application and run those three commands.
I use main as my branch name. In your case, it might be something different so it's not a mistake, you just might be using something else. Go ahead and run this and after that when you go back here and refresh you should be seeing the ShadCNUI Monorepo template readme file and in here you will see your apps, web and widget and you will also see your packages here math, eslint-config, typescript-config and UI. Perfect! So I believe that marks the end of this chapter.
We've learned a lot. I hope this made it easier for you to understand what we're building and how we're going to build this application. It is a little bit different. It might be outside of your comfort zone but I believe we're going to go through this together and build an amazing app. Great job!
See you in the next chapter. There.