📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Next.js 13 - The Basics

Beyond Fireship9:00

Transcription

The most popular full stack JavaScript framework in the world is without a doubt Next.js, and just recently they announced version 13, which unleashes a brand new paradigm for writing Next apps with React Server Components. Today's video is a full tutorial where we build a basic CRUD app for taking notes that takes advantage of these brand new features. It's beginner level, so you'll learn a lot if you're new to writing code, or if you're an experienced Next developer, you'll gain a better understanding of these new changes.

Whenever a popular framework has a major change, it's very difficult for those learning to code. I saw it happen when Angular 1 went to Angular 2, when Vue 2 went to Vue 3, and now with Next 12 to Next 13. There are millions of Next tutorials and courses on the internet, but as of this week, they're all outdated. Well, kind of. These new features can be incrementally adopted, which means you can still write Next code the old way. The bad news is that learning Next.js in 2023 will be a little more confusing because there are now two different ways to do essentially the same thing. Also, keep in mind that these new features are currently in beta, so if anything I show you today doesn't work, it's not my fault. Go and read the docs.

To get started, we'll first want to generate a new Next 13 app with a CLI, and I'll be using TypeScript with the `--ts` flag. Now, I want to build a real full stack app here, and that means we need some kind of backend database. For that, we'll be using PocketBase, which in my opinion is the ultimate backend for side projects. It's incredibly easy to use and can be a great option for production as well. To use it, simply go to the website and download its executable into the root of your project.

Now, open the terminal and run it with its `serve` command. It'll give you a link to the admin UI where you can now manage users and add records to a database. Let's go to the database section and add a new collection. Under the hood, PocketBase uses SQLite, so a collection is like a new table that will require a couple of fields like `title` and `content` for the notes. A database is secure by default, which means we'll need to go to the permissions and make all the different operations public. That's all we need for right now. Let's go ahead and get back into our Next.js code.

Currently, in the file system, you'll see a `pages` directory, but go ahead and put that in the garbage because that's the old way of doing things. The new way is to add your routes in the `app` directory. Next uses file system routing where the names of directories define the actual URL structure of your web app. When a directory is surrounded by brackets, that means it's a dynamic route, which might be like an ID or username that can be any wildcard value. In addition, directories can be surrounded by parentheses, which means they'll be ignored by the routing system. This can be useful when you want to place some files somewhere but don't want it to affect the actual URL structure.

Now, to build out the UI, there's a variety of different reserved file names, and the most common one that you'll use is `page.tsx`. It exports a default React component that defines the actual UI that you want to display here. That should give us a basic "Hello, World!". Let's go ahead and save the file and run the application. Before we do that, though, you should know that Vercel, the company that maintains Next.js, also just released a brand new build tool called Turbopack. It's currently in alpha, but I like to live dangerously. Go into `package.json` and add the `--turbo` flag to the `next dev` command.

Now, in the terminal, run `npm run dev`, and you should get that "Hello, World!" in the browser. What you'll notice is that it automatically detected that we were missing a `layout.tsx` file, which is the UI that surrounds the entire application. If we open it up, you can see it's a React component that takes `children` and renders them inside the `<body>` of the HTML document. Any code you define here will be displayed on every single page, so it might be a good place to add a global navbar and footer. What's cool about layouts is that they can be nested. When you have a layout in a subdirectory, it will only be applied to the children of that route, and you can also fetch data in layouts, which makes the application much more efficient because you don't need to re-render and refetch data on the sub-routes.

Next thing I'm doing is adding a global CSS file. I'm not going to explain the CSS in this tutorial, but you can copy it from the source code, which is on GitHub.

Now it's time to start thinking about routes. We can create a route called `/notes` by adding a new directory. This route will fetch all the notes from PocketBase and also provide a form to create a new note. First, to build out this UI, we'll add a `page.tsx` file inside of which exports a default React component that will also be marked `async`. The cool thing about Next 13 is that components are server components by default, which means they get rendered on the server, and we can do data fetching directly inside of them with `async/await`.

Define an `async` function called `getNotes` that uses the `fetch` API to retrieve data from your backend. In our case, PocketBase comes with a built-in REST API where we can point to the `notes` collection and then retrieve a paginated list of results. We'll say page 1 with 30 results per page. We can then convert that result into JSON and then return the `items`, which will be an array of the data in the database.

Now, in the component, to fetch data, all we have to do is `await` a call to this function. That gives us access to an array of notes, which we can loop over in the UI to render out a basic note component that contains the title, content, and so on. Go ahead and view the app in the browser, and you should now see a list of all the records you've created in the database. It's all server-rendered.

However, one important thing to understand is that Next will automatically cache this route because the route segment is not dynamic. In other words, it's treated like a static page. But we can go back into our code and change that by adding the `cache: 'no-store'` option to `fetch`, and now it will refetch the items from the server on every request. If you've used Next.js in the past, this is roughly the equivalent to `getServerSideProps`.

Now I want to point out that you don't have to use the `fetch` API here. PocketBase has its own SDK that works like an ORM, where we could just make a reference to it and then grab all the records by saying `db.records.getList('notes')`. But now you might be wondering, how do we change the caching behavior? Next 13 also has a variety of variables that you can export from a page to change things like the caching behavior and runtime, which is necessary if you are not using `fetch`.

So that takes care of our list page. Now I want to show you how to create a dynamic route. You'll notice in the note component that I'm linking to a note with a random ID, which is generated by PocketBase. To create a dynamic route like that, we'll add a second directory called `[id]` surrounded by brackets. That tells Next that this route segment is a wildcard and can be any value. Like before, we create a `page.tsx` component and export a server component from it. Let's also create a data fetching function called `getNote` that retrieves an individual item from the database, and in order to do that, it will need the ID from the URL. But let's once again use the `fetch` API and interpolate the `noteId` into the actual route.

Now I want to point out that because this is a dynamic route, it won't automatically cache every request. However, you may want to update the caching behavior. You can implement incremental static regeneration by adding the `revalidate` option to `fetch`. This tells Next to regenerate the page on the server if it's older than a certain number of seconds. And if you want to pre-render these pages, you can also export a function called `generateStaticParams`. This is the equivalent to `getStaticPaths` in previous versions of Next.

Now, in the component, we can call this function and pass the `params.id` from the URL as the argument. That gets the data from the database, which we can then display in the UI. At this point, you should be able to go to the list of notes, click on one of them, which navigates you to a page just for that one note. That's pretty cool.

But Next 13 also has a couple of other tricks up its sleeve to manage the loading state and UI between routes. It can add a `loading.tsx` file inside the `[id]` directory. It simply exports a component with some kind of loading indicator that will be rendered in place of the page when the data is being fetched. This results in faster load times because the content can be streamed directly into that component instead of waiting for the entire page to load. On a similar note, you can also have an `error.tsx` file that will also be rendered in place of the page when an error occurs, and that just makes it really easy to handle loading and error states.

So far in this tutorial, we've only looked at data fetching, but your app will also likely need to write or mutate data as well. In order to handle that, we'll need an interactive component that's rendered on the client. In the `/notes` directory, let's go ahead and create a new component called `CreateNote`. But unlike our other components, this one has `'use client'` at the top. This tells Next not to render it on the server, rather only in the browser.

In the component, I'm adding the `useState` hook to add fields for `title` and `content`. Then, in the JSX, I create a form that adds a couple of inputs to modify those values. Each input listens to the `onChange` event, then updates the state accordingly. Now, let's define a `create` function that's called when the form is submitted. It will also use `fetch` to make a request to the PocketBase API. However, this time it's a `POST` request and sends the data from the form as the body of the request.

Now, let's go ahead and take this client component and declare it in the `/notes` page. If we open up the app, we can now fill out this form and create a new item in PocketBase. However, in order to see that item in the list, we need to refresh the entire page, and that's not ideal. Luckily, the new Next router has us covered.

If we go back to the client component, we can import the `router` from `next/navigation`, and it has a special method called `refresh`. After the new record is created in the database, it'll re-execute the query on the `/notes` page to get the list of notes, which means you will automatically see the new note updated in the list without a full page refresh.

Congratulations, you just built a full stack application with Next 13. As of today, Next 13 is in beta and still a little rough around the edges, but I will keep you updated as things change. So make sure to subscribe to the channel. Thanks for watching, and I will see you in the next one.