Tutorials on Next.js

Learn about Next.js from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

How to use urql authExchange to implement authentication in Next.js

In this article, we will learn how to use the urql authExchange to add authentication with a GraphQL API in a Next.js app.In the last article , we have learnt how to use urql to execute queries and mutations in a Next.js Server-Side Rendered app. In the following sections, we will recap those learnings and build on them to implement authentication using urql exchanges. urql is a lightweight, versatile and extensible GraphQL client for modern frontend apps, with support for React, Svelte, Vue and plain JavaScript. It was introduced as an alternative to existing GraphQL clients like Relay and Apollo . Every query or mutation in urql is modeled as an 'operation', and the system at any moment has a stream of operations issued by various parts of the app. Exchanges are pieces of middleware that transform the stream of operations into a stream of results. This is explained in more detail in the architecture documentation . Some of urql 's core features such as fetching data and caching are also handled via exchanges implemented by the urql team and provided by default. You can also create your own exchanges by implementing functions that conform to the rules defined here . Next.js requires Node to be pre-installed on your system. You can then scaffold a Next.js TypeScript app using the following command in your terminal/command prompt. Once you have a skeleton app set up, you can install the dependencies required for urql . graphql is a peer dependency of urql and provides the underlying GraphQL implementation. No additional type definitions are required since urql is written in TypeScript. next-urql provides the Next.js bindings for urql . react-is is a peer dependency of next-urql , required for react-ssr-prepass to walk the component tree and pre-fetch any data required for rendering. @urql/exchange-auth provides the authExchange that we will implement our API authentication with. Finally, jwt-decode will be used to read the token and determine expiration time. We will also use Material UI to quickly scaffold out a login/register component and one that renders user info. Let us install the required dependencies for Material UI. Let us use the withUrqlClient HOC to wrap our entire app in the urql context. This makes the urql client and hooks usable in the rest of our app. The first parameter to withUrqlClient is a function that returns a ClientOptions object. This can be used to pass configuration into the urql client instance, such as the API URL, custom fetch function, request policy and any additional middleware in the exchanges property. For this tutorial, we will use the Web Collections Demo API from Formidable. In this tutorial, we will build a simple app with two screens - a login/register screen where users will authenticate or sign up to the app, and a user info screen that displays information about the logged in user. We will use the following operations from the Web Collections Demo API: We will use the browser's local storage to persist tokens across page reloads. The authExchange is a piece of middleware for the urql client, which intends to facilitate the typical JSON Web Token (JWT) based authentication flow with a simple and flexible API. It is provided in the @urql/exchange-auth package. We can add the authExchange to the exchanges property when configuring our urql client. The authExchange itself takes four pieces of configuration: The signin , register and refreshCredentials mutations from our API return an object that has the following shape. Let us implement a small helper function that stores the auth state in the browser's local storage. We've added a check for window since we only want this code to run in the browser. When the user logs out, we'd like to clear the auth state from storage. We can now add a couple of convenience functions to fetch the token and refresh token. Let us start implementing the authExchange functions, beginning with getAuth - this function needs to handle a couple of different scenarios: The addAuthToOperation function receives two parameters - the first is the current authState , and the second is the operation that will be executed next. The second parameter is of type Operation and is urql's way of representing an item in the stream of GraphQL requests. Operations have a context that contains the fetch options, either as an object or as an initializer function. We will retrieve the options for the current operation like so. We'll then create a new operation which is basically a clone of the input operation, along with our required override - the addition of our access token to the Authorization header. So the entire implementation of the addAuthToOperation function will look like this: Our API uses the GraphQL error code extension to communicate errors. For the purposes of this tutorial, we will only consider UNAUTHORIZED errors. Token expiry is one of the scenarios when we know the API will error. We are able to determine this by decoding the JWT and reading the exp field which specifies the expiration time. Our API's access token specifies the expiration time as seconds since the Unix epoch. Let us add a buffer of 5 seconds to determine that the token is about to expire. Now, this would work if all of our queries and mutations required the access token to be provided. However, we have mutations such as login and register which do not require the token passed in. We will need to adapt our logic to account for these mutations, and this can be done using the passed in Operation object. The full implementation of the willAuthError function will be as follows. Now that we have implemented all the required functions, the final step is to configure the urql client with the authExchange . Let us create a factory function that receives the ssrExchange and returns a client configuration object. We'll use this on our app's homepage in the next section. Our home page will render the login form when unauthenticated, and a user profile when the user is logged in. We'll use our clientOptions factory function here to provide the urql client to our app. We'll use two mutations login and register from the API in this component. urql 's useMutation hook can be used to consume these mutations within a React component. The useMutation hook returns a tuple containing the current state of the operation, and a function that triggers it. We'll trigger our mutations on submission of the respective form. Here is the full implementation of the page. We'll use the me query from the API to retrieve the user's ID, name and creation time. We can then use urql 's useQuery hook to execute the query. Let us create a simple layout with material-ui components to display the profile data. This will also include a loading indicator and an alert to show any error messages. In this article, we have learnt how to implement JWT authentication with a GraphQL API using urql and @urql/exhange-auth with a Next.js app. We have understood how to apply authentication to queries and mutations, and built a simple app that allows login and registration. All the code used in this article is available on my GitHub .

Thumbnail Image of Tutorial How to use urql authExchange to implement authentication in Next.js

How to Consume a GraphQL API in Next.js with urql and next-urql

In this article, we will learn how to use urql to consume a GraphQL API in a Next.js SSR app.urql is a lightweight, versatile and extensible GraphQL client for modern frontend apps, with support for React, Svelte, Vue and plain JavaScript. It was introduced as an alternative to existing GraphQL clients like Relay and Apollo . These GraphQL clients are largely similar when it comes to setup and use. In the next section, we will see how they compare with each other on various parameters. All three GraphQL clients provide standard features such as queries, mutations, caching and subscriptions, while only differing slightly in implementation. urql 's defaults for configuration are slightly better than those for Apollo, and it has a lower entry barrier thanks to its thorough documentation and native support for features otherwise only available via third-party plugins. Additionally, urql has a powerful caching mechanism offers both normalized and document caching via the @urql/exchange-graphcache package. urql is built on the principle of having a lightweight core, extensible via middleware called exchanges. This makes it the smallest in bundle size as compared to the other options. A full, detailed comparison of the clients by different categories of features can be found on the urql website . Next.js is one of the most popular React-based frameworks, and urql has first class native support for it via the next-urql package. Apollo and Relay do not have official plugins with support for Next.js which means the implementation might change between releases of the framework, and any app that uses it will have to be constantly maintained to keep up. With next-urql , most of the boilerplate involved in setting up urql for Server-Side Rendering (SSR) with Next.js is already done for you. It provides convenience functions such as the withUrqlClient HOC which enables your SSR pages to pre-fetch data via GraphQL queries. Next.js requires Node to be pre-installed on your system. You can then scaffold a Next.js TypeScript app using the following command in your terminal/command prompt. Once you have a skeleton app set up, you can install the dependencies required for urql . graphql is a peer dependency of urql and provides the underlying GraphQL implementation. No additional type definitions are required since urql is written in TypeScript. next-urql provides the Next.js bindings for urql . react-is is a peer dependency of next-urql , required for react-ssr-prepass to walk the component tree and pre-fetch any data required for rendering. We can use the withUrqlClient HOC to wrap our entire app in the urql context. This makes the urql client and hooks usable in the rest of our app. The first parameter to withUrqlClient is a function that returns a ClientOptions object. This can be used to pass configuration into the urql client instance, such as the API URL, custom fetch function, request policy and any additional middleware in the exchanges property. For this tutorial, we will use the GitHub GraphQL API. This API requires you to authenticate using a personal access token. You can follow the steps described here to create one after logging in with your GitHub account. We can then configure our urql client to pass the token as part of the authorization header on each request to the API. Now that we have our urql client set up, let us look at how we can use it to connect to the GitHub API and fetch some data. We will build a simple component that will display a list of repositories, with each item showing a link to the repo, its name, star count and commit count. The component will look somewhat like this. To start, let us look at the GraphQL query that should be used. This query fetches the first 10 repositories for the current user (determined from the personal access token). For each repository, it includes the name, ID, URL, stargazer count and the number of commits to the main branch. There are various other fields that can be added to the query, as documented in the API reference . We can execute this query using the useQuery hook from urql . Since we're using TypeScript, let us model the API response with the correct expected types and use them as type parameters to useQuery . The response object returned by useQuery returns a number of useful items, out of which we will currently use the fetching flag which tells us whether or not the operation is still in progress, and the data property which contains the fetched data when available. Let us now add some simple UI to render the returned data. This Repositories component now fetches and renders a list of repositories with star and commit counts. So far, we've seen how to set up urql in a Next.js app and use it to query the GitHub GraphQL API. Let's now take it a step further and learn how to create mutations - these are API operations that can cause the data to change in the backend. For the purposes of this tutorial, we will implement the creation of an issue within a given GitHub repository. The GraphQL mutation to create an issue looks like this: This mutation takes three variables - the repository ID to create the issue in, the title and the body of the issue. On success, it returns an Issue object that can contain the number, title and body. So let us model the request variables and response, and create the mutation. The useMutation hook returns a tuple with two items - an object that exposes the current state of the mutation request, and a function that can be invoked with input variables to execute the actual mutation. Let us adapt our Repositories component to be able to call this mutation. We'll refactor and extract some of the code into an individual Repository component along the way. This is what the refactored Repositories component will look like. All the GraphQL types have been moved to a separate types module. And the individual Repository component now renders the list item, along with a button that invokes the createIssue mutation when clicked. Clicking the button creates an issue with a sample fixed title and body in the corresponding repo. Every query or mutation in urql is modeled as an 'operation', and the system at any moment has a stream of operations issued by various parts of the app. Exchanges are pieces of middleware that transform the stream of operations into a stream of results. This is explained in more detail in the architecture documentation . Some of urql 's core features such as fetching data and caching are also handled via exchanges implemented by the urql team and provided by default. You can also create your own exchanges by implementing functions that conform to the rules defined here . Server-Side Rendered apps need to be set up to fetch data on the server-side and send it down to the client for hydration.  urql  supports this via the ssrExchange . The SSR exchange has two functions - it gathers all the data as it is being fetched server-side, and using the serialized data on the client side to rehydrate the app without a refetch. When using next-urql , most of the boilerplate involved in instantiating the ssrExchange is already done for you. So if your client does not use any other exchanges, you do not explicitly need to instantiate the ssrExchange when creating the client. To enable SSR, you simply need to set the ssr flag in the second argument to the client configuration function. If you do want to add other exchanges to your client, they can be specified in an exchanges property returned by the configuration function. This function also gets the instance of the ssrExchange passed into it when called. Enabling SSR when wrapping the top level App component in withUrqlClient disables Next's ' Automatic Static Optimization ' which allows for hybrid apps with both server-side rendered and statically generated pages. If this is required in your app, you can wrap individual page components with withUrqlClient as required. When applying withUrqlClient to specific pages, we can also use getStaticProps or getServerSideProps to pre-fetch the data and populate the urql cache. This will render the page as a static page, further optimizing performance and allowing us to perform other operations in these functions. Let us adapt our app to use server-side rendering with getServerSideProps for our repositories component. We will add getServerSideProps to our Home page component, as this function can only be exported from page components. The getServerSideProps function gets called when the page is being rendered server-side. It will populate the cache so that the subsequent render of the Repositories component will hydrate it from cache when useQuery is called. In this article, we have learnt how to set up urql with a Next.js app and perform queries and mutations against the GitHub GraphQL API. Further, we have also learnt about the architecture of urql and how Exchanges work. We have used next-urql to implement server side rendering with pre-fetching of queries using the urql client and cache. In a subsequent tutorial, we will learn how to use urql exchanges for authentication and caching. All the code used in this article is available on my GitHub .

Thumbnail Image of Tutorial How to Consume a GraphQL API in Next.js with urql and next-urql

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More

Static Site Generation with Next.js and TypeScript (Part V) - Build Time Access Tokens and Exporting Static HTML

Disclaimer - Please read the fourth part of this blog post here before proceeding. It demonstrates how to statically generate pages with dynamic routes using the getStaticPath() function. If you just want to jump straight into this tutorial, then clone the project repository and install the dependencies. In the previous part of this tutorial series, we encountered a big problem: each getStaticProps() and getStaticPath() function required us to obtain an access token before being able to request any data from the Petfinder API. This meant that anytime we built the Next.js application for production, we had to obtain several access tokens for the Petfinder API: If we were to add more statically generated pages to the Next.js application that depend on data from the Petfinder API, then we would continue to accumulate more access tokens that are scattered throughout the Next.js application. Unfortunately, the Next.js's custom <App /> component does not support data fetching functions like getStaticProps() and getStaticPath() . This means we don't have the option of obtaining a single access token, fetching all of the necessary data (e.g., a list of pet animal types and lists of recently adopted pets) in the getStaticProps() function of the custom <App /> component and passing the data as props to every page component at build time. One way to make the access token globally available to all page components at build time is to inject it as an environment variable. Below, I'm going to show you how to build a Next.js application with a single access token. We will obtain an access token from the Petfinder API via the cURL CLI tool, set it to an environment variable named PETFINDER_ACCESS_TOKEN and execute the npm run build command with this environment variable. Then, I'm going to show you how to export the Next.js application to static HTML. This allows us to deploy and serve the Next.js application on fast, static hosting solutions like Cloudflare Pages and GitHub Pages , all without ever having to spin up a Node.js server. To get started, clone the project repository and install the dependencies. If you're coming from the fourth part of this tutorial series, then you can continue from where the fourth part left off. Within the project directory, create a Makefile : With a Makefile, we can define rules that each run a set of commands. Rules are similar, purpose-wise, to npm scripts in package.json files. Each rule consists of, at a minimum, a target and a command. The target is the name of the rule, and the command is the actual command to execute. Inside of the Makefile , add two rules: dev and build . Note : Each indentation should be a tab that's four spaces wide. Otherwise, you may encounter the error *** missing separator. Stop. . Here, invoking the make command with the dev rule as the target ( make dev ) runs npm run dev , and invoking the make command with the build rule as the target ( make build ) runs npm run build . The Makefile allows us to store the result of shell commands into variables. For example, suppose we add the following line to the top of the Makefile . In the above example, we set the variable PETFINDER_ACCESS_TOKEN to the output of the echo command, which is the string "abcdef." The shell function performs command expansion, which means taking a command as an argument, running the command and returning the command's output. Once the shell function returns the command's output, we assign this output to the simply expanded variable PETFINDER_ACCESS_TOKEN . Anytime we reference a simply expanded variable, whose value is assigned with := , the variable gets evaluated once (at the time of assignment) and procedurally, much like what you would expect in a typical, imperative programming language like JavaScript. So if we were to reference the variable's value with $() , then the value will just be the string "abcdef." ( Makefile ) GNU make comes with another "flavor" of variable, recursively expanded variable , which evaluates a variable's value completely different than what most developers are used to. It's out of the scope of this tutorial, but you can read more about them here . If you print the value of the PETFINDER_ACCESS_TOKEN environment variable in the <HomePage /> page component's getStaticProps() function, then you will see the value "abcdef" logged in the terminal when you... ( pages/index.tsx ) Note : The PETFINDER_ACCESS_TOKEN environment variable's name will not be prefixed with NEXT_PUBLIC_ . Notice that the command (along with the value of environment variables passed to it), PETFINDER_ACCESS_TOKEN=abcdef npm run dev gets logged to the terminal. To tell make to suppress this echoing , you can prepend @ to lines that you want suppressed. For simple commands like echo , you can suppress the echoing by prepending @ to the command itself, like so: However, because the command we want suppressed begins with an environment variable, we wrap the entire command in @() , like so: ( Makefile ) When you re-run the make dev command, PETFINDER_ACCESS_TOKEN=abcdef npm run dev no longer gets logged to the terminal. To obtain an access token from the Petfinder API via cURL, you must send a request to the POST https://api.petfinder.com/v2/oauth2/token endpoint with the grant type ( grant_type ), client ID ( client_id ) and client secret ( client_secret ). This data can be passed by specifying a single -d option (short for --data ) as a concatenated string of key=value pairs (delimited with an ampersand) or multiple -d options, providing a key=value pair for each one. Here's what using the single -d option looks like: And here's what using the multiple -d options looks like: Here, we will use the single -d option. When you run the cURL command, you will see that the access token is returned in stringified JSON. We can pluck the access token from this stringified JSON by piping the output of the cURL command (the stringified JSON) to a sed command. On Unix-based machines, the sed command performs many types of text processing tasks, from search to substitution. The -E option (short for the --regexp-extended option) tells the sed command to find a substring based on an extended regular expression , which requires special characters to be escaped if you want to match for them as literal characters. When you run the cURL command with the piped sed command, you will see that only the access token is returned. Within the Makefile , let's set the PETFINDER_ACCESS_TOKEN variable to the cURL command with the piped sed command, like so: To pull the NEXT_PUBLIC_PETFINDER_CLIENT_ID , NEXT_PUBLIC_PETFINDER_CLIENT_SECRET and NEXT_PUBLIC_PETFINDER_API_URL environment variables from the .env file, we can use the include directive to pause reading from the current Makefile and read from the .env file before resuming. Then, with the export directive, we can export the environment variables that were read from the .env file. ( Makefile ) When you re-run the make dev command and visit http://localhost:3000/ in a browser, you will find that the access token is immediately available to the <HomePage /> page component's getStaticProps() function. Now we can remove all instances of fetching an access token within getStaticProps() and getStaticPath() functions from the Next.js application, and pass the PETFINDER_ACCESS_TOKEN environment variable to Authorization header of any request that's sent to the Petfinder API. Also, you can now remove the console.log({ PETFINDER_ACCESS_TOKEN }) line from the <HomePage /> page component's getStaticProps() function. ( pages/index.tsx ) ( pages/types/[type].tsx ) By passing the access token as an environment variable to the Next.js application, the Next.js application now makes ten fewer requests. Like with any solution, this approach does come with a caveat. Since an access token from the Petfinder API expires one hour from the time it was issued, a caveat of this approach is that you will have to reset the development server every hour to refresh the access token. To export the Next.js application to static HTML , we must add an export npm script to the package.json file that: ( package.json ) Add a new rule named export_html to the Makefile that runs the export npm script with the PETFINDER_ACCESS_TOKEN environment variable: ( Makefile ) Note : Remember, export is already a GNU make  directive. Therefore, you cannot name the rule export . When you run the make export_html command, you will find that the Next.js application could not be exported as static HTML because it makes use of the image optimization feature, which requires a Node.js server. To resolve this problem, we need to set experimental.images.unoptimized to true in the Next.js configuration to disable the image optimization feature. Specifically, we only want to disable this feature when the NEXT_EXPORT environment variable is present. The NEXT_EXPORT environment variable will only be set when exporting the Next.js application to static HTML. ( Makefile ) ( next.config.js ) When you re-run the make export_html command, the Next.js application will be exported to static HTML. Inside the project directory, you will find the exported HTML in an out directory: To test the static pages, you can spin up a standalone HTTP server that serves the contents of the out directory: If you visit http://localhost:8080/types/horse in a browser and disable JavaScript, then you will see that this page has already been pre-rendered at build time. If you find yourself stuck at any point during this tutorial, then feel free to check out the project's repository for this part of the tutorial here . Proceed to the next part of this tutorial series to dive into building interactive, client-side features for the Next.js application. If you want to learn more advanced techniques with TypeScript, React and Next.js, then check out our Fullstack React with TypeScript Masterclass :

Thumbnail Image of Tutorial Static Site Generation with Next.js and TypeScript (Part V) - Build Time Access Tokens and Exporting Static HTML

Static Site Generation with Next.js and TypeScript (Part IV) - Dynamic Routes with getStaticPaths

Disclaimer - Please read the third part of this blog post here before proceeding. It walks through image optimization in a Next.js application with the <Image /> component of next/image and Blurhash, an algorithm that generates beautiful, lightweight, canvas-based placeholder images. If you just want to jump straight into this tutorial, then clone the project repository and install the dependencies. When you look at client-side routing solutions for single-page applications, such as React Router for React-based applications, you will find that they require you to define each route and manually map each one to a specific page component. With Next.js, you have a file-system based router that automatically maps each page component within the pages directory to a unique route. No extra code or routing library is needed. The route of a page is determined by the location of its page component within the pages directory. For example, if the page component <Blog /> is defined within the file pages/blog/index.tsx (or pages/blog.tsx ), then you can access it at the route /blog . The Next.js router can handle several different types of routes: If your Next.js application depends on external data to statically generate pages for dynamic routes, such as from a content management system (CMS) for blog articles, then you can export a getStaticPaths() function that specifies the paths to generate at build time, like so: Therefore, if this was placed in a pages/posts/[id].tsx file, then for each post from the JSONPlaceholder API, Next.js will pre-render a static page that can be accessed at the route /posts/:id (e.g., /posts/1 ). In the route, the named parameter id gets replaced with the value of a param with a matching name ( id: post.id ). Since the JSONPlaceholder API returns 100 posts, Next.js will pre-render 100 static pages and routes. With fallback set to false , any requests to /posts/:id outside of the range of pre-generated routes of /posts/1 to /posts/100 will return a 404 page. As for the content of the page, you will still need to export a getStaticProps() function to fetch any data that's needed to pre-render the page's content. Note : The getStaticPaths() function can be used only when the page component uses the getStaticProps() function. As for client-side navigation from one route to another, Next.js, like React Router, comes with a <Link /> component that lets users navigate to other application routes without the browser triggering a full-page reload. Below, I'm going to show you how to pre-generate paths for dynamic routes-based pages with the getStaticPaths() function. For each pet animal type, we will be adding a static page ( /types/:type ) that lists the most recently adopted pets. By the end of this tutorial, the application will gain eight more static pages, all accessible from the home page: To get started, clone the project repository and install the dependencies. If you're coming from the third part of this tutorial series, then you can continue on from where the third part left off. Within the project directory, let's install several dependencies: Since the Petfinder API returns a pet's description as stringified HTML markup, these two dependencies will help us clean up the HTML markup and prepare it so that it is safe to display to users. Let's install several dev. dependencies: According to the Petfinder API documentation , each pet returned by the API comes with photos of various sizes ( small , medium , large and full ) that are hosted on the photos.petfinder.com and dl5zpyw5k3jeb.cloudfront.net (likely subject to change in the future) domains. Therefore, we need to add the photos.petfinder.com and dl5zpyw5k3jeb.cloudfront.net domains to the list of whitelisted image domains in the Next.js configuration. In the case that the Petfinder API does not return an image for a pet, we will display a generic placeholder image, which will come from the via.placeholder.com domain. And so, we will also need to add the via.placeholder.com domain to the list of whitelisted image domains in the Next.js configuration: ( next.config.js ) First, let's create a types directory under the pages directory. Within this newly created directory, create a [type].tsx file. This file will contain a page component that will be served for the route /types/:type . The named parameter type will be replaced with slugified pet animal types, such as dog and small-furry . Within this file, let's define a page component named <TypePage /> , and export two functions, getStaticProps() and getStaticPaths() . ( pages/types/[type].tsx ) The getStaticPath() function will need to send a request to the GET /types endpoint of the Petfinder API to fetch the available pet animal types and generate a path for each one. The getStaticProps() function will need to send a request to three endpoints of the Petfinder API: And pass all of the returned data as props to the <TypePage /> component so that the component can render a list of the most recently adopted pets and a list of available breeds for the specific pet animal type. However, remember that for us to interact with the Petfinder API, we must first obtain an access token. This access token must be attached to the Authorization header of any subsequent request to the Petfinder API so that we have the necessary permissions for receiving data from the Petfinder API. The getStaticProps() function's context argument provides the values of the named parameters in the route via a params object. ( pages/types/[type].tsx ) Note : Unlike the getStaticProps() function that receives a context object as an argument, the getStaticPaths() function does not receive any argument. One thing you will notice is that if we decide to add more pages to this application, then anytime those pages require us to fetch data from the Petfinder API, we would have to obtain a new access token for each getStaticProps() / getStaticPaths() function. This is an incredibly wasteful, especially if we only need one access token to statically generate the pages at build time. Is there a way that we can obtain one access token, and use this one access token for fetching data from the Petfinder API across every getStaticProps() / getStaticPaths() function in a Next.js application? Unfortunately, Next.js's <App /> component does not support data-fetching functions like getStaticProps() (and by extension, getStaticPath() ) and getServerSideProps() , so we can't even consider obtaining an access token and passing data, such as a list of pet animal types, as props to every page component at build time. A possible solution is to leverage a Makefile that obtains an access token by using cURL , and set the access token as an environment variable so that the Next.js application can directly access the access token anywhere. We will explore this in the next part of this tutorial series. With all of the necessary data fetched from the Petfinder API, let's create two components: <AnimalCard /> and <AnimalCardsList /> . The <AnimalCard /> component will render a card that displays information about a pet, such as its name, breed, adoption status, contact information of the organization that's currently sheltering and caring for the pet, etc. The <AnimalCardsList /> component will render a list of <AnimalCard /> components in a single-column layout. Within the <TypePage /> page component, let's update the main heading with the pet type and render the <AnimalCardsList /> component under the second <section /> element. We set the animals prop of the <AnimalCardsList /> component to the list of recently adopted pets fetched from the Petfinder API ( adoptedAnimals ). ( pages/types/[type].tsx ) Now, inside of the components/AnimalCardsList.tsx file, let's define the <AnimalCardsList /> component: ( components/AnimalCardsList.tsx ) Finally, inside of the components/AnimalCard.tsx file, let's define the <AnimalCard /> component. Most of the icons used in the card come from heroicons . ( components/AnimalCard.tsx ) Let's test that everything works by spinning up the Next.js application in development mode. Inside of a browser, visit http://localhost:3000/types/horse . You will find that Next.js has successfully generated a page that belongs to the dynamic routes for /types/:type . When you generate the static pages at build time ( npm run build ), you will notice a static page for each pet animal type under the .next/server/pages/types directory. When you spin up the statically generated site in production mode ( npm run start ) and visit any of the static pages (e.g., http://localhost:3000/types/horse ), you will notice that no network requests are made to the Petfinder API and that the page has already been pre-rendered. To help with page navigation, let's add breadcrumbs to the top of the <TypePage /> component. Create a <Breadcrumbs /> component that takes a list of pages as props and renders a breadcrumb for each page. ( components/Breadcrumbs.tsx ) Then, add the <Breadcrumbs /> component to the <TypePage /> component, like so: ( pages/types/[type].tsx ) When you re-run the Next.js application, you will find the breadcrumbs at the top of the page. If you find yourself stuck at any point during this tutorial, then feel free to check out the project's repository for this part of the tutorial here . Proceed to the next part of this tutorial series to learn how to obtain a single access token at build time that can be used across every getStaticProps() / getStaticPath() function in a Next.js application. Plus, we will further flesh out the <TypePage /> component with client-side rendered content. If you want to learn more advanced techniques with TypeScript, React and Next.js, then check out our Fullstack React with TypeScript Masterclass .

Thumbnail Image of Tutorial Static Site Generation with Next.js and TypeScript (Part IV) - Dynamic Routes with getStaticPaths

Static Site Generation with Next.js and TypeScript (Part VI) - Client-Side Rendering

Disclaimer - Please read the fifth part of this blog post here before proceeding. It covers how to efficiently build a Next.js application with a single access token that can be used across all getStaticProps() and getStaticPath() functions. It also covers how to export a Next.js application to static HTML. If you just want to jump straight into this tutorial, then clone the project repository and install the dependencies. If all rendering happened on the client-side, then you end up with several problems. For example, suppose you build an application with Create React App . If you disable JavaScript in the browser and reload the application, then you will find the page void of content since React cannot run without JavaScript. Therefore, checking the DOM in the developer console, the <div id="root" /> element, where React renders all of the dynamic content, will be shown to be empty. There's also the possibility of the client running on an underpowered device, so rendering might take longer than expected. Or worse, the client has a poor network connection, so the application might have to wait longer for JavaScript bundles and other assets to be fully fetched before being able to render anything to the page. This is why it's important to not blindly render all content with only one rendering strategy. Rather, you should consider taking a hybrid approach when building an application. By having some content pre-rendered in advance via static-site generation (or server-side rendering) and having the remaining content rendered via client-side rendering, you can simultaneously deliver both a highly performant page and an enriching user experience. Thus far, every page of our Next.js application has been pre-rendered: the / and /types/:type pages. The initial HTML of the home page ( / ) contains the markup of the eight pet animal type cards that each allows users to navigate to a list of recently adopted pets. The initial HTML of each type page ( /types/:type ) contains the markup of the list of recently adopted pets. Once the page's initial HTML gets loaded and rendered, the browser hydrates the HTML. This breathes life into the page, giving it the ability to respond to user events and enabling features like client-side navigation and lazy-loading. Below, I'm going to show you how to render dynamic content on the client-side of a Next.js application with the useEffect Hook. When the user visits the /types/:type page, the list of pet animals available for adoption will be fetched and rendered on the client-side. By delegating the rendering of this list to the client-side, we reduce the likelihood of the list being outdated and including a pet that might have been adopted very recently. To get started, clone the project repository and install the dependencies. If you're coming from the fifth part of this tutorial series, then you can continue on from where the fifth part left off. Within the <TypePage /> component, let's define two state variables and their corresponding setter methods: ( pages/types/[type].tsx ) To fetch a list of adoptable pet animals, let's call a useEffect Hook that fetches this list upon the <TypePage /> component mounting to the DOM. The type prop is passed to the dependency array of this Hook because its id property is accessed within the Hook. ( pages/types/[type].tsx ) When you run the Next.js application in development mode ( make dev ) and visit the page http://localhost:3000/types/horse , you will encounter the following error message: Since the PETFINDER_ACCESS_TOKEN environment variable is not prefixed with NEXT_PUBLIC , the PETFINDER_ACCESS_TOKEN environment variable is not exposed to the browser. Therefore, we will need to fetch a new access token for sending requests to the Petfinder API on the client-side. ( pages/types/[type].tsx ) To communicate to users that the list of adoptable pet animals is being fetched from the Petfinder API, let's add a <Loader /> component and show it to the user anytime the page is updating this list. To the right of the animated spinner icon, the <Loader /> component shows a default "Loading ..." message. With the className and children props, you can override the <Loader /> component's styles and the default "Loading..." message respectively. ( components/LoadingSpinner.tsx ) Now let's add a new section to the <TypePage /> component that displays the list of adoptable pet animals. Additionally, let's update the <TypePage /> component accordingly so that the <Loader /> component is shown when the isUpdatingAdoptableListings flag is set to true . ( pages/types/[type].tsx ) When the Next.js application refreshes with these changes, fully reload the page. The list of recently adopted pet animals (above-the-fold content) instantly gets rendered since it was pre-rendered at build time. If you quickly scroll down the page, you can momentarily see the loader just before it disappears and watch as the client renders the fetched list of adoptable pet animals (below-the-fold content). Suppose the page is kept open for some time. The initial list of adoptable pet animals will become outdated. Let's give the user the ability to manually update this list by clicking an "Update Listings" button. ( components/UpdateButton.tsx ) When the user clicks on the button, and the handleOnClick method sets the isUpdating flag to true ... Within the <TypePage /> component, let's move the fetchAdoptableAnimals function out of the useEffect Hook so that it can also be called within the updateAdoptableListing() function, which gets passed to the handleOnClick prop of the <UpdateButton /> component. By wrapping the updateAdoptableListing() function in a useCallback Hook, the function is memoized and only gets recreated when the prop type changes, which should never change at any point during the lifetime of the <TypePage /> component. Therefore, anytime a state variable like adoptableAnimals or isUpdatingAdoptableListings gets updated and causes a re-render of the component, the fetchAdoptableAnimals() function will not be recreated. ( pages/types/[type].tsx ) Upon the initial page load, a simple animated spinner icon with the text "Loading..." is shown to the user as the client fetches for a list of adoptable pet animals. Then, anytime the user clicks on the "Update Listings" button to update the list of adoptable pet animals, an overlay with an animated spinner icon gets placed on top of the previous listings. This preserves the previous listings in case the client cannot update the list of adoptable pet animals (due to a network error, etc.). Let's make a few adjustments to the <TypePage /> component to ensure that the previous listings are kept in state when the client fails to update the list of adoptable pet animals. We add another state variable isUpdateFailed that's set to true only if an error was encountered when fetching an updated list of adoptable pet animals. When isUpdateFailed set to true , a generic error message "Uh Oh! We could not update the listings at this time. Please try again." is shown to the user. ( pages/types/[type].tsx ) If you find yourself stuck at any point during this tutorial, then feel free to check out the project's repository for this part of the tutorial here . Please stay tuned for future parts, which will cover topics like API routes and deployment of the Next.js application to Vercel! If you want to learn more advanced techniques with TypeScript, React and Next.js, then check out our Fullstack React with TypeScript Masterclass :

Thumbnail Image of Tutorial Static Site Generation with Next.js and TypeScript (Part VI) - Client-Side Rendering

Static Site Generation with Next.js and TypeScript (Part III) - Optimizing Image Loading with Plaiceholder and BlurHash

Disclaimer - Please read the second part of this blog post here before proceeding. It explains the different data-fetching techniques that Next.js supports, and it guides you through the process of statically rendering a Next.js application page that fetches data at build time via the getStaticProps function. If you just want to jump straight into this tutorial, then clone the project repository and install the dependencies. Slow page loading times hurt the user experience. Anytime a user waits longer than a few seconds for a page's content to appear, they usually lose their patience and close out the page in frustration. A significant contributor to slow page loading times is image sizes. The larger an image is, the longer it takes the browser to download and render it to the page. One way to improve the perceived load time of images (and by extension, page) is to initially show a placeholder image to the user. This image should occupy the same space as the intended image to prevent cumulative layout shifting . Additionally, compared to the intended image, this image should be much smaller in size (at most, several KBs) so that it loads instantaneously (within the window of the page's first contentful paint). The placeholder image can be as simple as a single, solid color (e.g., Google Images or Medium) or as advanced as a blurred representation of the image (e.g., Unsplash). For Next.js application's, the <Image /> component from next/image augments the <img /> HTML element by automatically handling image optimizations, such as... These optimizations make the page's content immediately available to users to interact with. As a result, they help to improve not only a page's Core Web Vitals , but also the page's SEO ranking and user experience. Next.js <Image /> components support blurred placeholder images. To tell Next.js to load an image with a blurred placeholder image, you can either... If the Next.js application happens to load images from an external service like Unsplash, then for each image, would we need to manually create a blurred placeholder image, convert the blurred placeholder image into a Data URL and pass the Data URL to the blurDataUrl prop? With the Plaiceholder library, we can transform any image into a blurred placeholder image, regardless of where the image is hosted. The blurred placeholder image is a very low resolution version of the original image, and it can be embedded within the page via several methods: CSS, SVG, Base64 and Blurhash. Blurhash is an algorithm that encodes a blurred representation of an image as a small, compact string. Decoding this string yields the pixels that make up the blurred placeholder image. Using these pixels, you can render the blurred placeholder image within a <canvas /> element. Blurhash allows you to render a blurred placeholder image without ever having to send a network request to download it. Below, I'm going to show you how to improve image loading in a Next.js application with the Plaiceholder library and Blurhash. To get started, clone the project repository and install the dependencies. If you're coming from the second part of this tutorial series, then you can continue on from where the second part left off. Within the project directory, let's install several dependencies: Then, wrap the Next.js configuration with withPlaiceholder to extend the Next.js Webpack configuration so that Webpack excludes the sharp image processing library from the output bundle. ( next.config.js ) Specifying images.domains in the Next.js configuration restricts the domains that remote images can come from. This protects the application from malicious domains while still allowing Next.js to apply image optimizations to remote images. Currently, the images on the home page are high resolution and take a lot of time to be fully downloaded. In fact, without any image processing, some of the raw Unsplash images are as large as 20 MBs in size. However, by replacing these images with more optimized images, we can significantly improve the performance of the Next.js application. Using the <Image /> component from next/image and the Plaiceholder library, the home page will... These changes should shrink the bars in the developer console's network waterfall chart. To generate the blurred placeholder images, let's first modify the <HomePage /> component's getStaticProps() function so that these images get generated at build time. For each pet animal type, call the getPlaiceholder() method of the Plaiceholder library with one argument: a string that references the image. This string can be a relative path or an absolute URL. Here, the string will be an absolute URL since the images come directly from Unsplash. From the getPlaiceholder() method, we can destructure out any of the following: For the Next.js application, we will destructure out blurhash for a Blurhash-based blurred placeholder image and img for information about the actual image. ( pages/index.tsx ) Note #1 : The getPlaiceholder() method can accept a second, optional argument that lets you override the default placeholder size, configure the underlying Sharp library that Plaiceholder uses for image processing, etc. Check the Plaiceholder documentation here for more information. Note #2 : If you append the query parameters fm=blurhash&w=32 to the URL of an Unsplash image, then the response returned will be a Blurhash encoded string for that Unsplash image. Then, we need to update the shared/interfaces/petfinder.interface.ts file to account for the newly added properties ( blurhash and img ) on the AnimalType interface. ( shared/interfaces/petfinder.interface.ts ) After updating the shared/interfaces/petfinder.interface.ts file, let's replace the image that's set as a CSS background image in the <TypeCard /> component with two components: Run the Next.js application in development mode: When you visit the Next.js application in a browser, you will notice that the blurred placeholder images immediately appear. However, when the optimized images are loaded, you will notice that some of them are not aligned correctly within their parent containers: To fix this, let's use some of the <Image /> component's advanced props to instruct how an image should fit (via the objectFit prop) and be positioned (via the objectPosition prop) within its parent container. For these props to work, you must first set the <Image /> component's layout prop to fill so that the image can grow within its parent container (in both the x and y axes). The objectFit and objectPosition props correspond to the CSS properties object-fit and object-position . For objectFit , set it to cover so that the image occupies the entirety of the parent container's space while maintaining the image's aspect ratio. The outer portions of the image that fail to fit within the parent container will automatically be clipped out. For objectPosition , let's assign each pet animal type a unique objectPosition that positions the pet animal within the image to the center of the parent container. Any pet animal type that is not assigned a unique objectPosition will by default have objectPosition set to center . ( enums/index.ts ) ( pages/index.tsx ) ( shared/interfaces/petfinder.interface.ts ) When you re-run the Next.js application, you will notice that the images are now correctly aligned within their parent containers. When you check the terminal, you will encounter the following warning message being logged: Since we specified the layout prop as fill for the <Image /> component within the <TypeCard /> component, we tell the <Image /> component to stretch the image until it fills the parent element. Therefore, the dimensions of the image don't have to be specified, which means the height and width props should be omitted. Let's make the following changes to the <TypeCard /> component in components/TypeCard.tsx : Unsplash leverages Imgix to quickly process and deliver images to end users via a URL-based API . Based on the query parameters that you append to an Unsplash image's URL, you can apply various transformations to the image, such as face detection , focal point cropping and resizing . Given the enormous base dimensions of the Next.js application's Unsplash images (e.g., the dog image is 5184px x 3456px), we can resize these images to smaller sizes so that Plaiceholder can fetch them faster. Since the images occupy, at most, a parent container that's 160px x 160px, we can resize the initial Unsplash images so that they are, at most, 320px in width (and the height will be resized proportionally to this width based on the image's aspect ratio). Let's append the query parameter w=320 to the Unsplash image URLs in enums/index.ts . ( enums/index.ts ) When you re-run the Next.js application, you will notice that the Next.js application runs much faster now that Plaiceholder doesn't have to wait seconds to successfully fetch large Unsplash images. If you find yourself stuck at any point during this tutorial, then feel free to check out the project's repository for this part of the tutorial here . Proceed to the next part of this tutorial series to learn how to create pages for dynamic routes in Next.js. If you want to learn more advanced techniques with TypeScript, React and Next.js, then check out our Fullstack React with TypeScript Masterclass .

Thumbnail Image of Tutorial Static Site Generation with Next.js and TypeScript (Part III) - Optimizing Image Loading with Plaiceholder and BlurHash

Static Site Generation with Next.js and TypeScript (Part I) - Project Overview

Many of today's most popular web applications, such as G-Mail and Netflix, are single-page applications (SPAs). Single-page applications deliver highly engaging and exceptional user experiences by dynamically rendering content without fully reloading whole pages. However, because single-page applications generate content via client-side rendering, the content might not be completely rendered by the time a search engine (or bot) finishes crawling and indexing the page. When it reaches your application, a search engine will read the empty HTML shell (e.g., the HTML contains a <div id="root" /> in React) that most single-page applications start off with. For a smaller, client-side rendered application with fewer and smaller assets and data requirements, the application might have all the content rendered just in time for a search engine to crawl and index it. On the other hand, for a larger, client-side rendered application with many and larger assets and data requirements, the application needs a lot more time to download (and parse) all of these assets and fetch data from multiple API endpoints before rendering the content to the HTML shell. By then, the search engine might have already processed the page, regardless of the content's rendering status, and moved on to the next page. For sites that depend on being ranked at the top of a search engine's search results, such as news/media/blogging sites, the performance penalties and slower first contentful paint of client-side rendering may lower a site's ranking. This results in less traffic and business. Such sites should not client-side render entire pages worth of content, especially when the content infrequently (i.e., due to corrections or redactions) or never changes. Instead, these sites should serve the content already pre-generated as plain HTML. A common strategy for pre-generating content is static site generation . This strategy involves generating the content in advance (at build time) so that it is part of the initial HTML document sent back to the user's browser when the user first lands on the site. By exporting the application to static HTML, the content is created just once and reused on every request to the page. With the content made readily available in static HTML files, the client has much less work to perform. Similar to other static assets, these files can be cached and served by a CDN for quicker loading times. Once the browser loads the page, the content gets hydrated and maintains the same level of interactivity as if it was client-side rendered. Unlike Create React App , popular React frameworks like Gatsby and Next.js have first-class, built-in static site generation support for React applications. With the recent release of Next.js v12, Next.js applications build much faster with the new Rust compiler (compared to Babel, this compiler is 17x faster). Not only that, Next.js now lets you run code on incoming requests via middleware, and its APIs are compatible with React v18. In this multi-part tutorial, I'm going to show you how to... We will be building a simple, statically generated application that uses the Petfinder API to display pets available for adoption and recently adopted. All of the site's content will be pre-rendered in advance with the exception of pets available for adoption, which the user can update on the client-side. Home Page ( / ): Listings for Pet Animal Type ( /types/<type> ): Visit the live demo here: https://petfinder-nextjs.vercel.app/ To get started, initialize the project by creating its directory and package.json file. Note : If you want to skip these steps, then run the command npx create-next-app@latest --ts to automatically scafford a Next.js project with TypeScript. Then, proceed to the next section of this tutorial. Install the following dependencies and dev. dependencies: Add a .prettierrc file with an empty configuration object to accept Prettier's default settings. Add the following npm scripts to the package.json file: Here's what each script does: At the root of the project directory, create an empty TypeScript configuration file ( tsconfig.json ). By running next , Next.js automatically updates the empty tsconfig.json file with Next.js's default TypeScript configuration. ( tsconfig.json ) Note : If you come across the error message Error: > Couldn't find a `pages` directory. Please create one under the project root , then at the root of the project directory, create a new pages directory. Then, re-run the npm run dev command. Note : Double-check the configuration, and make sure that the moduleResolution compiler option is not missing and set to node . Otherwise, you will encounter the TypeScript error Cannot find module 'next' . Currently, this approach generates a TypeScript configuration that has the strict compiler option set to false . If you bootstrapped the project via the create-next-app CLI tool ( --ts / --typescript option), then the strict compiler option will be set to true . Let's set the strict compiler option to true so that TypeScript enforces stricter rules for type-checking. ( tsconfig.json ) Note : Setting strict to true automatically enables the following seven type-checking compiler options: noImplicitAny , noImplicitThis , alwaysStrict , strictBindCallApply , strictNullChecks , strictFunctionTypes and strictPropertyInitialization . Additionally, this command auto-generates a next-env.d.ts file at the root of the project directory. This file guarantees Next.js types are loaded by the TypeScript compiler. ( next-env.d.ts ) To further configure Next.js, create a next.config.js file at the root of the project directory. This file allows you to override some of Next.js's default configurations, such as the project's base Webpack configurations and mapping between incoming request paths and destination paths. For now, let's just opt-in to React's Strict Mode to spot out any potential problems, such as legacy API usage and unsafe lifecycles, in the application during development. ( next.config.js ) Similar to the tsconfig.json file, running next lint automatically installs the eslint and eslint-config-next development dependencies. Plus, it creates a new .eslintrc.json file with Next.js's default ESLint configuration. Note : When asked "How would you like to configure ESLint?" by the CLI, select the "Strict" option. ( eslintrc.json ) This application will be styled with utility CSS rules from the Tailwind CSS framework . If you are not concerned with how the application is styled, then you don't have to set up Tailwind CSS for the Next.js application and can proceed to the next section. Otherwise, follow the directions here to properly integrate in Tailwind CSS. To register for a Petfinder account, visit the Petfinder for Developers and click on "Sign Up" in the navigation bar. Follow the registration directions. Upon creating an account, go to https://www.petfinder.com/developers/ and click on the "Get an API Key" button. The form will prompt you for two pieces of information: "Application Name" and "Application URL" (at the minimum). For "Application Name," you can enter anything as the application name (e.g., "find-a-cute-pet" ). For "Application URL," you can enter https://<application_name>.vercel.app since the application will be deployed on Vercel by the end of this tutorial series. To see if an https://<application_name>.vercel.app URL is available, visit the URL in a browser. If Vercel returns a 404: NOT_FOUND page with the message <application_name>.vercel.app might be available. Click here to learn how to assign it to a project. , then the application name is likely available and can be used for completing the form. You can find the API key (passed as the client ID in the request payload to the POST https://api.petfinder.com/v2/oauth2/token endpoint) and secret (passed as the client secret in the request payload to the POST https://api.petfinder.com/v2/oauth2/token endpoint) under your account's developer settings. Here, you can track your API usage. Each account comes with a limit of 1000 daily requests and 50 requests per second. At the root of the project directory, create a .env file with the following environment variables: ( .env ) Replace the Xs with your account's unique client ID and secret. The home page features a grid of cards. Each card represents a pet animal type catalogued by the Petfinder API: These cards lead to pages that contain listings of pets recently adopted and available for adoption, along with a list of breeds associated with the pet animal type (e.g., Shiba Inu and Golden Retriever for dogs). Suppose you have to build this page with client-side rendering only. To fetch the types of animal pet from the Petfinder API, you must: Initially, upon visiting the page, the user would be presented with a loader as the client... Having to wait on the API to process these two requests before any content is shown on the page only adds to a user's frustrations. Wait times may even be worst if the API happens to be experiencing downtime or dealing with lots of traffic. You could store the access token in a cookie to avoid sending a request for a new access token each time the user loads the page. Still, you are left with sending a request for a list of types each time the user loads the page. Note : For stronger security (i.e., mitigate cross-site scripting by protecting the cookie from malicious JavaScript code), you would need a proxy backend system that interacts with the Petfinder API and sets an HttpOnly cookie with the access token on the client's browser after obtaining the token from the API. More on this later. This page serves as a perfect example for using static site generation over client-side rendering. The types returned from the API will very rarely change, so fetching the same data for each user is repetitive and unnecessary. Rather, just fetch this data once from the API, build the page using this data and serve up the content immediately. This way, the user does not have to wait on any outstanding requests to the API (since no requests will be sent) and can instantly engage with the content. With Next.js, we will leverage the getStaticProps function, which runs at build time on the server-side. Inside this function, we fetch data from the API and pass the data to the page component as props so that Next.js pre-renders the page at build time using the data returned by getStaticProps . Note : In development mode ( npm run dev ), getStaticProps gets invoked on every request. Previously, we created a pages directory. This directory contains all of the Next.js application's page components. Next.js's file-system based router maps page components to routes. For example, pages/index.tsx maps to / , pages/types/index.tsx maps to /types and pages/types/[type.tsx] maps to types/:type ( :type is a URL parameter). Now let's create four more directories: The Petfinder API documentation provides example responses for each of its endpoint. With these responses, we can define interfaces for the responses from the following endpoints: Create an interfaces directory within the shared directory. Inside of the interfaces directory, create a petfinder.interface.ts file. ( shared/interfaces/petfinder.interface.ts ) Note : This tutorial skips over endpoints related to organizations. Inside of the pages directory, create an index.tsx file, which corresponds to the home page at / . To build out the home page, we must first define the <HomePage /> page component's structure. ( pages/index.tsx ) Then, create the <TypeCardsGrid /> component, which renders a grid of cards (each represents a pet animal type). This component places the cards in a... ( components/TypeCardsGrid.tsx ) Then, create the <TypeCard /> component, which renders a card that represents a pet animal type. This component shows a generic picture of the pet animal type and a link to browse listings (recently adopted and available for adoption) of pet animals of this specific type. Note : The types returned from the Petfinder API do not have an id property, which serves as both a unique identifier and a URL slug (e.g., the ID of type "Small & Furry" is "small-furry"). In the next section, we will use the type name to create an ID. ( components/TypeCard.tsx ) Since the Petfinder API does not include an image for each pet animal type, we can define an enumeration ANIMAL_TYPES that supplements the data returned from the API with an Unsplash stock image for each pet animal type. To account for the images' different dimensions in the <AnimalCard /> component, we display the image as a background cover image of a <div /> and position the image such that the animal in the image appears in the center of a 10rem x 10rem circular mask. ( .enums/index.ts ) For users to browse the pet animals returned from the Petfinder API for a specific pet animal type, they can click on a card's "Browse Listings" link. The link is wrapped with a Next.js <Link /> component (from next/link ) to enable client-side navigation to the listings page. This kind of behavior can be found in single-page applications. When a user clicks on a card's "Browse Listings" link, the browser will render the listings page without having to reload the entire page. The <Link /> component wraps around an <a /> element, and the href gets passed to the component instead of the <a /> element. When built, the generated markup ends up being just the <a /> element, but having an href attribute and having the same behavior as a <Link /> component. To apply TailwindCSS styles throughout the entire application, we need to override the default <App /> component that Next.js uses to initialize pages. Next.js wraps every page component with the <App /> component. This is also useful for other reasons like... To override the default <App /> component, create an _app.tsx file within the pages directory. Inside of this file, import the styles/globals.css file that contains TailwindCSS directives, and define an <App /> component, like so: ( pages/_app.tsx ) The <App /> component takes two props: To verify that the homepage works and that the TailwindCSS styles have been applied correctly, let's run the application: Within a browser, visit localhost:3000 . Currently, the <TypeCardsGrid /> is not rendered since we have not yet fetched any pet animal types from the Petfinder API. If you find yourself stuck at any point during this tutorial, then feel free to check out the project's repository for this part of the tutorial here . Proceed to the next part of this tutorial series to learn how to fetch this data from the Petfinder API with the getStaticProps function. If you want to learn more advanced techniques with TypeScript, React and Next.js, then check out our Fullstack React with TypeScript Masterclass :

Thumbnail Image of Tutorial Static Site Generation with Next.js and TypeScript (Part I) - Project Overview

Web Components in Server-Side Rendered (SSR) and Static-Site Generated (SSG) in Next.js Applications

Compared to other web development frameworks, Next.js has become very popular because of its support for a number of rendering strategies that result in highly performant frontend applications: By having the content readily available in the initial HTML document, less client-side rendering takes place. All the client has to do is hydrate the components and make them functional. The less dynamic content the client renders, the better the application's performance and SEO. Search engine bots like Googlebot can visit the application, immediately crawl (and understand) its content and rank it high on search engines. If you use Web Components to build your application's UI, then you don't need to worry about the breaking changes that third-party JavaScript frameworks/libraries notoriously cause (as a result of rapid development and release cycles). Your application performs equally as well as (or better than) applications that use these frameworks/libraries. Additionally, the low-level, native APIs of Web Components are based on an official W3 specification and let you develop encapsulated, modular and reusable components (represented as custom HTML elements). However, you will come across several limitations that can prevent the application from taking advantage of these rendering strategies: Custom web components are defined with a class that extends from HTMLElement and registered using the customElements.define method. Since browser APIs do not exist in the context of Node.js, Next.js's build tools cannot run code that uses these APIs. This also applies to the Shadow DOM . The Shadow DOM's imperative nature prevents custom web components from being rendered by either the server or build tools. The missing browser APIs can be "polyfilled" and "shimmed" for these environments before the custom web component is defined with the class keyword. This approach makes it possible to simulate the DOM and output an HTML string for the initial HTML document, but it lacks the ability to hydrate components. Fortunately, the Declarative Shadow DOM provides an alternative way for implementing web components that complements server-side rendering and static-site generation. Below, I'm going to show you how to pre-render custom web components in Next.js. By extracting the logic used to create the template that is attached to the component's innerHTML into a separate module, the server (or build tools) never has to run code that uses browser APIs like HTMLElement and customElements . For any custom web component that has a shadow root attached to it, we will use the Declarative Shadow DOM so that the shadow root is attached to it and readily available at the time of the component's instantiation. To get started, scaffold a new Next.js application with the TypeScript boilerplate template. Once the application has been scaffolded, change the current directory to the application's directory, like so: In this tutorial, we want to test that the custom web components are pre-rendered when the application is server-side rendered or static-site generated. Next.js provides several CLI commands for building and running the application: dev , build , start and lint . For server-side rendering, we want to build a production version of the application and spin up a Next.js production server that runs getServerSideProps on each incoming request and pre-renders this application with the returned props. Since next start starts the server, let's add a prestart npm script that runs the build npm script, which builds the application via the next build command. ( package.json ) For static-site generation, we want to export the application to static HTML. Let's add an export npm script that exports the application to static HTML via the next export command. Since the application must be built prior to being exported, let's also add a preexport npm script that runs the build npm script. ( package.json ) Within the pages/index.tsx file, remove the children elements under the <main /> element, and replace the text within the <footer /> element's child <a /> element with the text "Powered by Vercel." ( pages/index.tsx ) Now that there are no more <Image /> components in the application, let's remove the import Image from 'next/image' line from the pages/index.tsx file. This Next.js application will feature two custom web components: Since the <pinned-location-link /> component will not use a Shadow DOM, let's create this component such that its rendering logic (what's set to innerHTML ) is completely decoupled from its class declaration. First, create a directory named components , which will house these custom web components. Within this directory, create two subdirectories, pinned-location-link and info-tooltip , each of which will contain two files: Let's define and register the <pinned-location-link /> component. To keep things simple, components' props will only come from their attributes. ( components/pinned-location-link/component.ts ) Note #1 : When using these components in JSX/TSX code, attributes must be prefixed with data- (custom data attributes). Otherwise, TypeScript will raise the following error: Property '<attribute>' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>' . Note #2 : connectedCallback is a special lifecycle callback that gets called after the custom element is inserted into the DOM. Note #3 : To fix the TypeScript error Type 'NamedNodeMap' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators. on [...this.attributes] , set the downlevelIteration option to true under the compilerOptions in the tsconfig.json file. This allows you to iterate over iterators in TypeScript code. Attributes for any node are represented as a NamedNodeMap , not as an Array . The connectedCallback lifecycle callback... Since all JSX elements come with a dangerouslySetInnerHTML property, this approach allows Next.js to not have to run the component.ts file to set the component's innerHTML . You don't have to polyfill and shim browser APIs like HTMLElement and customElements for Next.js's build tools. Let's write the template function that generates the component's stringified HTML from the passed props. ( components/pinned-location-link/template.ts ) This template function supports several types of location search queries used in a Google Maps URL, each of which is optional: Based on the passed props, we can add a class to the <a /> element in the stringified HTML. We can also modify the Google Maps URL and the text displayed within its child <span /> element. Inside of the <main /> element of the <Home /> page component in the pages/index.tsx file, add the <pinned-location-link /> component, like so: ( pages/index.tsx ) Currently, Next.js doesn't know what the HTML content inside of the <pinned-location-link /> component is supposed to be. However, if we give it the dangerouslySetInnerHTML property and set the __html key to the result of the component's template function, then Next.js can render the component's HTML content without ever having to define or register it as a custom element. To demonstrate this, let's import the template function from the components/pinned-location-link/template.ts file and add the dangerouslySetInnerHTML property to the component, like so: ( pages/index.tsx ) Note : If any data comes from a third-party service, then you may want to pass the result of template to an HTML sanitizer like DOMPurify to mitigate cross-site scripting (XSS) attacks. Unfortunately, TypeScript raises the following error: Property 'pinned-location-link' does not exist on type 'JSX.IntrinsicElements' . Since the <pinned-location-link /> component is not a React component or standard HTML element, TypeScript does not recognize it as a valid JSX element. Therefore, we must create a type definition file ( types/index.d.ts ) that lets us add custom elements to the JSX.IntrinsicElements global interface: ( types/index.d.ts ) With the TypeScript error no longer popping up in VSCode, let's build the application and run it in production mode: Note : Add export {}; to the empty components/info-tooltip/component.ts and components/info-tooltip/template.ts files. Otherwise, the build will fail. Within a browser, visit localhost:3000 . Then, disable JavaScript and reload the page. When you inspect the elements on the page, you will see that the HTML content of the <pinned-location-link /> component still exists despite JavaScript being disabled. Now, let's define and register the <info-tooltip /> component. Unlike the <pinned-location-link /> component, this component will use the Shadow DOM. A hidden, separate DOM will be attached to the component (the custom element becomes a shadow host). All CSS rules defined within the Shadow DOM will be scoped to elements that are part of the shadow tree, and CSS rules outside of the Shadow DOM that specify the same selectors will have no effect on these elements. The Shadow DOM will be implemented declaratively, not imperatively. The Declarative Shadow DOM involves wrapping the component's content in a <template /> element with a shadowroot attribute. When this attribute is set to open , the shadow root's internal features can be accessed with JavaScript. On the other hand, imperatively setting up the shadow root involves calling the Element.attachShadow method to attach a shadow tree to the custom element. ( components/info-tooltip/component.ts ) Since both the <pinned-location-link /> and <info-tooltip /> components extract props from data attributes, we should refactor this logic into a function named extractPropsFromAttrs so that it can be shared by both components. ( shared/index.ts ) Don't forget to update the components/pinned-location-link/component.ts file so that the connectedCallback lifecycle callback calls this method. ( components/pinned-location-link/component.ts ) Once you have made that update, create the <info-tooltip /> component's template. The <info-tooltip /> component only accepts two props: ( components/info-tooltip/template.ts ) Unlike the <pinned-location-link /> component's template, the <info-tooltip /> component's template gets wrapped within a <template /> element by default during server-side rendering or static-site generation. If the Declarative Shadow Root does not exist when the page is initially loaded, then via client-side rendering, the shadow root gets set up imperatively with the Element.attachShadow method. The <template /> element is only for the Declarative Shadow DOM. For us to use it in the JSX code, add the <info-tooltip /> custom element to the JSX.IntrinsicElements global interface. ( types/index.d.ts ) With everything completed, let's render this component within the <Home /> page component. Import the template function from the components/info-tooltip/template.ts file and add the dangerouslySetInnerHTML property to the component, like so: Note : Since both files, components/info-tooltip/template.ts and components/pinned-location-link/template.ts , export a template function as the default export, both functions will need to be renamed to avoid conflicting function names. Let's rebuild the application and run it in production mode: Within a browser, revisit localhost:3000 . JavaScript should still be disabled from before. When you inspect the elements on the page, you will see that the HTML content of the <info-tooltip /> component lives within a Shadow DOM despite JavaScript being disabled. Suppose we re-enable JavaScript. If we add data attributes to the <pinned-location-link /> and <info-tooltip /> components rendered in the <Home /> page component, then the components should initially be rendered with the props passed to the template function for the dangerouslySetInnerHTML property. Once client-side rendering takes place, the components should be rendered with the props extracted from the data attributes. Let's add some data attributes to these components. ( pages/index.tsx ) Then, rebuild the application and run it in production mode: When you reload the page, you will see that nothing has changed. This is because we have not yet imported the component.ts files into the Next.js application. Since the server and Next.js build tools do not run the useEffect lifecycle hook, let's dynamically import these files in useEffect . After the initial render of the page, useEffect will run on the client-side, import these files and execute them. These custom elements will officially be registered by the browser, and the component will be updated and re-rendered using the data attributes. ( pages/index.tsx ) Then, let's temporarily modify the components/info-tooltip/component.ts file so that regardless of whether or not the Declarative Shadow Root exists as a result of SSR/SSG, the <info-tooltip /> component's content will be updated using the props extracted from the component's data attributes: ( components/info-tooltip/component.ts ) Rebuild the application once more and run it in production mode: When you reload the page, you will see that the content of the components is initially rendered via the dangerouslySetInnnerHTML property. As soon as the client fetches the component.ts files and executes them, the content of the components updates accordingly with the data attributes set on these components. If you disable JavaScript and reload the page again, then the client never fetches the component.ts files, and you fallback to the components in their initial render state once more. Afterwards, undo the changes made to the components/info-tooltip/component.ts file. Now let's test that the application works properly when static-site generated. Run the following command to export the application as static HTML: With the static HTML (e.g., index.html and 404.html files) exported to an out directory by default, serve the directory as a static site via serve : Within a browser, visit localhost:3000 and reload the page with JavaScript enabled/disabled to verify everything still works properly. Currently, only Google Chrome and Microsoft Edge natively support Declarative Shadow DOM. To provide support for this feature in other browsers, we must use the @webcomponents/template-shadowroot ponyfill . Unlike polyfills, which are found in HTML document's <head /> , ponyfills are found at the end of the HTML document's <body /> . Since it gets executed after the DOM is fully loaded, the ponyfill will be able to find all the <template /> elements that have a shadowroot attribute and convert them into shadow roots on the <template /> elements' parent elements (done via the ponyfill's hydrateShadowRoots method). In the Next.js application, we will dynamically import the ponyfill in the useEffect lifecycle hook after the <Home /> page component's initial render. Once the ponyfill has been successfully imported, we check if the client natively supports Declarative Shadow DOM. If not, then we call hydrateShadowRoots on the HTML document's <body /> . Afterwards, we proceed to dynamically import the two component.ts files like before. First, let's install the @webcomponents/template-shadowroot ponyfill: Then, dynamically import the ponyfill in the useEffect lifecycle hook, like so: ( pages/index.tsx ) If you find yourself stuck at any point while working through this tutorial, then feel free to visit the main branch of this GitHub repository here for the code. Try passing data to custom web components from props returned by getServerSideProps and getStaticProps . If you want to learn more advanced techniques with web components, then check out the Fullstack Web Components  book by Steve Belovarich, a software engineer who has many years of industry experience building out enterprise-grade web applications.

Thumbnail Image of Tutorial Web Components in Server-Side Rendered (SSR) and Static-Site Generated (SSG) in Next.js Applications

Fullstack React with TypeScript Masterclass is LIVE 🎉

The Fullstack React with TypeScript Masterclass is now live! 🎉   This Masterclass teaches you practical React and TypeScript for developing apps from idea to completion, along with all the important tools in the React ecosystem. It expands on the material taught in our comprehensive book,  Fullstack React with TypeScript , and gives you over 10 hours of video lessons taught by Maksim Ivanov. By the end of the first module, you'll already have created your environment for React with TypeScript, and you will have completed basic tasks with TypeScript. The subsequent modules then continue your journey through building multiple apps and learning techniques including: This masterclass was developed by Maksim Ivanov and Alex Bespoyasov and taught by Maksim. Maksim worked for leading game developer Mojang, where he helped develop front-end interfaces with React and TypeScript. He has continued his front-end work at Spotify, where he develops interfaces with React, TypeScript, and related tools.  Alex is a frontend developer and works with technology company 0+X where he consults on developing and maintaining applications with React and related tooling.  With their combined depth of expertise, Maksim and Alex will quickly get you up to speed on creating modern React and TypeScript apps that your users will love. You can read more details about the Masterclass over at the Fullstack React with TypeScript Masterclass page .

Thumbnail Image of Tutorial Fullstack React with TypeScript Masterclass is LIVE 🎉

Static Site Generation with Next.js and TypeScript - Project Overview

Many of today's most popular web applications, such as G-Mail and Netflix, are single-page applications (SPAs). Single-page applications deliver highly engaging and exceptional user experiences by dynamically rendering content without fully reloading whole pages. However, because single-page applications generate content via client-side rendering, the content might not be completely rendered by the time a search engine (or bot) finishes crawling and indexing the page. When it reaches your application, a search engine will read the empty HTML shell (e.g., the HTML contains a <div id="root" /> in React) that most single-page applications start off with. For a smaller, client-side rendered application with fewer and smaller assets and data requirements, the application might have all the content rendered just in time for a search engine to crawl and index it. On the other hand, for a larger, client-side rendered application with many and larger assets and data requirements, the application needs a lot more time to download (and parse) all of these assets and fetch data from multiple API endpoints before rendering the content to the HTML shell. By then, the search engine might have already processed the page, regardless of the content's rendering status, and moved on to the next page. For sites that depend on being ranked at the top of a search engine's search results, such as news/media/blogging sites, the performance penalties and slower first contentful paint of client-side rendering may lower a site's ranking. This results in less traffic and business. Such sites should not client-side render entire pages worth of content, especially when the content infrequently (i.e., due to corrections or redactions) or never changes. Instead, these sites should serve the content already pre-generated as plain HTML. A common strategy for pre-generating content is static site generation . This strategy involves generating the content in advance (at build time) so that it is part of the initial HTML document sent back to the user's browser when the user first lands on the site. By exporting the application to static HTML, the content is created just once and reused on every request to the page. With the content made readily available in static HTML files, the client has much less work to perform. Similar to other static assets, these files can be cached and served by a CDN for quicker loading times. Once the browser loads the page, the content gets hydrated and maintains the same level of interactivity as if it was client-side rendered. Unlike Create React App , popular React frameworks like Gatsby and Next.js have first-class, built-in static site generation support for React applications. With the recent release of Next.js v12, Next.js applications build much faster with the new Rust compiler (compared to Babel, this compiler is 17x faster). Not only that, Next.js now lets you run code on incoming requests via middleware, and its APIs are compatible with React v18. In this multi-part tutorial, I'm going to show you how to... We will be building a simple, statically generated application that uses the Petfinder API to display pets available for adoption and recently adopted. All of the site's content will be pre-rendered in advance with the exception of pets available for adoption, which the user can update on the client-side. Home Page ( / ): Listings for Pet Animal Type ( /types/<type> ): Visit the live demo here: https://petfinder-nextjs.vercel.app/ To get started, initialize the project by creating its directory and package.json file. Note : If you want to skip these steps, then run the command npx create-next-app@latest --ts to automatically scafford a Next.js project with TypeScript. Then, proceed to the next section of this tutorial. Install the following dependencies and dev. dependencies: Add a .prettierrc file with an empty configuration object to accept Prettier's default settings. Add the following npm scripts to the package.json file: Here's what each script does: At the root of the project directory, create an empty TypeScript configuration file ( tsconfig.json ). By running next , Next.js automatically updates the empty tsconfig.json file with Next.js's default TypeScript configuration. ( tsconfig.json ) Additionally, this command auto-generates a next-env.d.ts file at the root of the project directory. This file guarantees Next.js types are loaded by the TypeScript compiler. ( next-env.d.ts ) To further configure Next.js, create a next.config.js file at the root of the project directory. This file allows you to override some of Next.js's default configurations, such as the project's base Webpack configurations and mapping between incoming request paths and destination paths. For now, let's just opt-in to React's Strict Mode to spot out any potential problems, such as legacy API usage and unsafe lifecycles, in the application during development. ( next.config.js ) Similar to the tsconfig.json file, running next lint automatically installs the eslint and eslint-config-next dev. dependencies. Plus, it creates a new .eslintrc.json file with Next.js's default ESLint configuration. Note : When asked "How would you like to configure ESLint?" by the CLI, select the "Strict" option. ( eslintrc.json ) This application will be styled with utility CSS rules from the Tailwind CSS framework . If you are not concerned with how the application is styled, then you don't have to set up Tailwind CSS for the Next.js application and can proceed to the next section. Otherwise, follow the directions here to properly integrate in Tailwind CSS. To register for a Petfinder account, visit the Petfinder for Developers and click on "Sign Up" in the navigation bar. Follow the registration directions. Upon creating an account, you can find the API key (passed as the client ID in the request payload to the POST https://api.petfinder.com/v2/oauth2/token endpoint) and secret (passed as the client secret in the request payload to the POST https://api.petfinder.com/v2/oauth2/token endpoint) under your account's developer settings. Here, you can track your API usage. Each account comes with a limit of 1000 daily requests and 50 requests per second. At the root of the project directory, create a .env file with the following environment variables: ( .env ) Replace the Xs with your account's unique client ID and secret. The home page features a grid of cards. Each card represents a pet animal type catalogued by the Petfinder API: These cards lead to pages that contain listings of pets recently adopted and available for adoption, along with a list of breeds associated with the pet animal type (e.g., Shiba Inu and Golden Retriever for dogs). Suppose you have to build this page with client-side rendering only. To fetch the types of animal pet from the Petfinder API, you must: Initially, upon visiting the page, the user would be presented with a loader as the client... Having to wait on the API to process these two requests before any content is shown on the page only adds to a user's frustrations. Wait times may even be worst if the API happens to be experiencing downtime or dealing with lots of traffic. You could store the access token in a cookie to avoid sending a request for a new access token each time the user loads the page. Still, you are left with sending a request for a list of types each time the user loads the page. Note : For stronger security (i.e., mitigate cross-site scripting by protecting the cookie from malicious JavaScript code), you would need a proxy backend system that interacts with the Petfinder API and sets an HttpOnly cookie with the access token on the client's browser after obtaining the token from the API. More on this later. This page serves as a perfect example for using static site generation over client-side rendering. The types returned from the API will very rarely change, so fetching the same data for each user is repetitive and unnecessary. Rather, just fetch this data once from the API, build the page using this data and serve up the content immediately. This way, the user does not have to wait on any outstanding requests to the API (since no requests will be sent) and can instantly engage with the content. With Next.js, we will leverage the getStaticProps function, which runs at build time on the server-side. Inside this function, we fetch data from the API and pass the data to the page component as props so that Next.js pre-renders the page at build time using the data returned by getStaticProps . Note : In development mode ( npm run dev ), getStaticProps gets invoked on every request. Now, within the root of the project directory, create a pages directory, which will contain all of the page components. Next.js's file-system based router maps page components to routes. For example, pages/index.tsx maps to / , pages/types/index.tsx maps to /types and pages/types/[type.tsx] maps to types/:type ( :type is a URL parameter). Create three more directories: The Petfinder API documentation provides example responses for each of its endpoint. With these responses, we can define interfaces for the responses of endpoints related to pet animals, pet animal types and pet animal breeds. Create an interfaces directory within the shared directory. Inside of the interfaces directory, create a petfinder.interface.ts file. ( shared/interfaces/petfinder.interface.ts ) Note : This tutorial skips over endpoints related to organizations. Inside of the pages directory, create an index.tsx file, which corresponds to the home page at / . Let's build out the home page by first defining the <HomePage /> page component's structure. ( pages/index.tsx ) Let's create the <TypeCardsGrid /> component, which renders a grid of cards (each represents a pet animal type). The component places the cards in a 4x2 grid layout for large screen sizes (width >= 1024px), 3x3 grid layout for medium screen sizes (width >= 768px), 2x4 grid layout for small screen sizes (width >= 640px) and a single column for mobile screen sizes (width < 640px). ( components/TypeCardsGrid.tsx ) Let's create the <TypeCard /> component, which renders a card that represents a pet animal type. The card shows a generic picture of the pet animal type and a link to browse listings (recently adopted and available for adoption) of pet animals of this specific type. Note : The types returned from the Petfinder API do not have an id property, which serves a both a unique identifier and a URL slug (e.g., the ID of type "Small & Furry" is "small-furry"). In the next section, we will create a helper method that takes a type name and turns it into an ID. ( components/TypeCard.tsx ) Since the Petfinder API does not include an image for each pet animal type, we can define an enumeration ANIMAL_TYPES that supplements the data returned from the API with an Unsplash stock image for each pet animal type. To account for the images' different dimensions in the <AnimalCard /> component, we display the image as a background cover image of a <div /> and position the image such that the animal in the image appears in the center of a 10rem x 10rem circular mask. ( .enums/index.ts ) Like single-page applications that don't fully reload the page when navigating between different pages, Next.js also lets you perform client-side transitions between routes via the Link component of next/link . This component wraps around an <a /> element, and the href gets passed to the component instead of the <a /> element. When built, the generated markup ends up being just the <a /> element, but having an href attribute and having the same behavior as a <Link /> component.

Thumbnail Image of Tutorial Static Site Generation with Next.js and TypeScript  - Project Overview

Deploying Next.js Application on Vercel, Heroku, and a Custom Static Server

In this post, we will be looking at Next.js app deployment on different types of servers and using different technologies, such as: We will go through deployment setup on each technology step by step and show the code. Some time ago web-developers shipped their applications in production by hand. They might use FTP or some other protocols to copy built application assets to production server. This approach has a lot of downsides. Automatic deployment solves these problems by extracting the shipping process from development. Thus, it makes it possible to ship applications consistently, continually and automatically. Deployment in general is all the processes required to make an application available for use. In a case with a web-app typical deployment usually consists of building an app and uploading its assets to a server (production or staging). Deployment is automatic if all those actions happen without human interaction. So if a developer has to build an application themselves it’s not automatic deployment. Automatic deployment has many advantages: In our case, the deployment will consist of building an app and delivering its assets to a server. At the end of this post you will learn how to set up automatic deployments with Vercel, Heroku, and on your own server via SSH from your GitHub repo. We suppose you have a GitHub account—it will be needed throughout the whole post. We will use pull-requests as triggers to deployments. Also, we suppose you know what Next.js is and how to create applications using it. If you don’t know it yet, you can learn it from the 5th chapter of “Fullstack React with TypeScript” book and for this tutorial you can use our app starter pack . For the last section of this tutorial, we suppose you have a server with SSH access to it. You’re going to need one to be able to allow GitHub and GitHub Actions to upload app assets. Vercel is a solution for deploying applications built with Next.js from creators of Next.js. As it put in official Next.js documentation , it is “the easiest way to deploy Next.js to production”. Let’s try it. To connect Vercel and start deploying you’re going to need a GitHub repository. It will allow connecting Vercel to codebase and trigger deployment on new commits in the master branch. If you already have a repository with an application built with Next.js you can skip this section. Create a new repo at the  “New” page . If you don’t want to create a repository, you can fork ours with app starter files. Clone the project repository on your local machine and open the project. If the repository is empty, add application code, commit and push it. Vercel account allows you to connect your projects’ repositories and monitor deployments as well as see the deployment history. Create an account on the signup page . If you already have an account, you can skip this section. When you created an account you should have access to Vercel’s Dashboard . The dashboard is like a control panel. Here you will be able to see the last deployments of every application and recent activity of an imported project. Let’s import the project repo. Find the “Import project” button and hit it. You will be redirected to the “Import” page . On that page find the “Import Git Repository” section. Click ”Continue“ button in that section. You will see a form with an input that requires an URL of a git repository. Enter your project’s repository URL there and submit the form. When it’s done Vercel will redirect you to GitHub. There you will be asked by GitHub for permissions to repository. Allow Vercel read and write access to the selected repository. It is possible to allow third-party applications to read and write every repository you have, but not recommended by security reasons. Try to limit third-party application’s access as minimal as possible. After you grant permissions to Vercel, GitHub will redirect you back to the “Import” page. There you will see import settings: Keep the project name the same as the repository name to make it less ambiguous. The last two options may be useful if you have, for example, a custom build script. Let’s say, to build a project you use instead of standard npm run build some another command. In that case, you can override default command with your own in the “Build and Output Settings” section. Same with the “Environment variables”. Sometimes you might need to configure the build process from outside. Usually, it is done with environment variables that are passed via command line. The most often example is NODE_ENV variable, which configures what kind of build should be triggered. Its value set to production usually tells that this is a production build and should be optimized. In a case with Next.js application, we don’t need to configure anything except for a project name. When you set it up, hit the “Deploy” button. You will see the congratulations screen with a “Visit” link, which will lead to a freshly deployed app on vercel.app domain. Hit this link to open and inspect the current deployment. And that’s it! You just deployed an application and made it available for users! Now, return to the Dashboard for a minute. The new project will appear in a list of your imported applications. Click a link to your project. On a project page, you will see the ”Production Deployment“ section. It contains information about current production deployment, such as: Below, you should see a “Preview Deployments” section. This section contains non-production deployments, such as staging and testing deployments. Let’s say you want to test some features in the environment very close to production, but don’t want to ship an untested feature in production. Preview deployments will help you do that. By default to create a preview deployment, you need to create a pull-request (or merge-request) to default branch in your repository. Vercel will analyze the state of a repo and if there are some pull-requests to the default branch, it will deploy every new commit in this pull-request as preview deployment. To test it, create a new branch in the repository. Checkout to this branch on your local repo, make some changes in your code, commit and push them to the created branch. After that create a pull-request from a new branch to default. When it’s done, Vercel-bot will automatically deploy this pull-request as a preview and leave a comment right in pull-request. You won’t even need to return to the Dashboard to inspect your project, the link to preview will be in the bot’s comment! And of course, the preview deployment will appear in the list of preview deployments in Dashboard. Heroku is a container-based cloud platform for deploying and managing applications. It also allows you to automate deploys and trigger them by pushing to repository's default branch. The first step is basically the same as in the previous section. You’re going to need a GitHub repository. New commits in the master branch will trigger deployments on Heroku as well. If you already have a repository with an application built with Next.js you can skip this section. Create a new repo at the “New” page . If you don’t want to create a repository, you can fork ours with app starter files . Clone the project repository on your local machine and open the project. If the repository is empty, add application code, commit and push it. Heroku also allows you to monitor your connected apps and their activity. To connect Heroku with your GitHub repository you’re going to need a Heroku account. Go to the signup page and create an account. If you already have one, you can skip this section. When you have an account on Heroku, you should have access to its Dashboard . The Dashboard contains lists of all the connected apps and services. To create a new app find a “New” button and hit it. In a select chose “Create new app” option. It will redirect you to a page with the new app settings screen. There you will be able to choose a name for your app, and a region. The region can affect performance and download time. For example, for users in Europe app deployed to the US region might load a bit slower than for users in the US because of the distance a request should pass between a user and a server. When it’s done, add a new pipeline with the button below. A Heroku pipeline is a set of actions being performed over your application. Let’s say you want not to just deploy an app, but to test is first and only then deploy—this set of testing and deploying actions is a pipeline. Heroku Pipelines represent steps of the continuous delivery workflow. In this case you can select “Production” since we won’t run any tests and want to just deploy the application. After it’s done, you will be asked about the deployment method. There might be 3 options: The first one is convenient when you have a git repository and you want to deploy an app right from the command line . If you have a Heroku CLI installed, there is a special command for deploying from the command line: But since we’re using GitHub select the “Connect to GitHub” method. Then select a repository to connect to from a list below. You might be asked for repository permissions. Again, try to keep third-party app access as minimal as possible. When you grant permissions to Heroku you can set up automatic deploys for some branch. By default automatic deploys may be turned off, so don’t forget to check the checkbox in this section. When you turn them on, select a branch from which to deploy. By default it is master , but you can select any other branch in your repository. Optionally check “wait for CI to pass before deploy” to ensure your tests pass before a project goes to production if there are any. GitHub Actions are an automatization workflow for building, testing, deployment, and other routines. They allow you to create a custom life cycle for your app. So, you can setup code-linting, code-formatting, some code checks, and all. They are like robots that receive messages and do some stuff. Actions are being set up by YAML-files, that describe what to do and what triggers this action. To tell GitHub that there is an action that should be played, create a directory called .github , mind the dot in front of the title. This is the directory that contains all the actions and workflows for this repository. Inside of that directory create another one called workflows . A workflow is a set of actions. So if you want to chain some actions together in a list you can use a workflow. In workflows directory create a file called main.yml , this is the workflow. Open this file and paste this code inside: Let’s break it down. The first line describes the name of this workflow. When GitHub will run this workflow, in a workflow dashboard you will see a list of all created workflows. The next directive is on . It describes what events should trigger this workflow. In this case, workflow should be triggered on push event in the master branch of this repo. So when someone pushes to master branch this workflow will be played. For deployment with Heroku, there is an action called “Deploy to Heroku”. Open its page and scroll to the “Getting Started” section . There you will see an example of the workflow setup. Let’s examine it. jobs contains all the work to do. In this case, there is only one job to do— build . This job runs on ubuntu-latest , this is the type of a machine that runs a workflow. The steps directive describes what steps to perform. In the example, each step is a GitHub Action. Latter is being run with some arguments that are described with with directive. heroku_api_key should be generated and stored in the “Secrets” section in GitHub. For this, you’re going to need Heroku CLI. If you already have it installed you can skip this section. Heroku CLI makes it easy to create and manage your Heroku apps directly from the terminal. It allows you for example deploy right from your terminal. However in this case you need it for another reason. You need to generate heroku_api_key for the repository secrets section. This is done with Heroku CLI. To install Heroku CLI go to its page and select an OS that you use. Note that different OSs use different installation methods. When it’s done check if there are no errors by running: Then, authenticate with this command: You’ll be prompted to enter any key to go to your web browser to complete the login. The CLI will then log you in automatically. Thus, you will be authenticated in your terminal when you will use heroku CLI command. Notice YOUR_EMAIL in its response, this should be the same you set in heroku email of main.yml . To generate a new token open your terminal, check if Heroku CLI is installed and run As a response, you will get a generated token. Copy its value and create a new GitHub Secret with it. Go to the “Secrets” section in GitHub: Settings → Secrets → New Secret. Set HEROKU_API_KEY as a name and generated token as a value. Save this secret. GitHub will use this value and replace ${{secrets.HEROKU_API KEY}} with it at build time automatically. In your package.json update start script to be as an example below. Pay attention to $PORT environment variable, that must be specified . When it’s done you should be able to trigger deploys by pushing changes to the master branch. Try to update code and push to master . In Dashboard, you will see a new app in the list of apps. Click on it and you will be redirected to an “Overview” page. There you should see latest activity and all the settings for this project. Find the “Open App” button to visit your application and inspect it. Sometimes third-party solutions wouldn’t work. It might happen for many reasons: it can cost a lot, or due to security reasons, but it might happen. In this case, there is an option to deploy an application on your own server. In this section, we suppose you have a server with SSH access to it. It will be required later. The first step is basically the same as in the previous section. You’re going to need a GitHub repository. New commits in the master branch will trigger deploy. If you already have a repository with an application built with Next.js you can skip this section. Create a new repo at the  “New” page . If you don’t want to create a repository, you can fork ours with app starter files . Clone the project repository on your local machine and open project. If the repository is empty, add application code, commit and push it. You’re going to deploy an app via SSH. For this, there is an action called “ssh deploy” . It uses Node.js and integrates by YAML-file as other GitHub Action do. This GitHub Action deploys a specific directory from GITHUB_WORKSPACE to a folder on a server via rsync over ssh. This workspace is a directory that is created by checkout action we used before. The workflow will be: Let’s create a new file in .github/workflows directory called custom.yml . This is the file that will describe your new workflow. In this file write the name of the workflow and events that should trigger it. This code means that this workflow will be triggered on every new push in the master branch. The detailed explanation of every line of this code you can find in the section above. Then, describe jobs to do and steps. Here we tell GitHub to check out this code, it will create a workspace that can be accessed with GITHUB_WORKSPACE . The second action sets up Node.js with version 12. (LTS on the moment this post is being written.) Then describe build step: It will install all the dependencies, build the project, and export static files. next export allows you to export your app to static HTML , which can be run standalone without the need of a Node.js server. It works by prerendering all pages to HTML. The reason we use npx is because we didn’t install next CLI tools globally, so it won’t be found in GitHub Action runtime, which will cause an error. npx on the other hand will execute a local package's binary. The last step in the workflow is: It tells GitHub Actions to use ssh-deploy action and pass some of the environment variables: secrets. mean that the value will be requested in GitHub Secrets, and for this to work you're going to need to specify those secrets that should be stored in the “Secrets” section in your GitHub repository. Create 4 new secrets in your project. Go to the “Secrets” section in GitHub repository and create: Connect to your server via SSH. (You may be asked a password if you connect the first time.) When connected, generate a new key pair. Keep the passphrase empty. Specify the type of the key as RSA. You might want to set some unique name for the key, just to make it easier to find it later, so when a command-line ask you how to name the file, you can change it. When generated, authorize those keys, otherwise, the server might not allow “ssh-deploy” to connect. Note the ~/.ssh/key-name —that’s the full path to the key file. It may vary depending on your file structure on a server. Now copy the private key value and paste it as a value for SERVER_SSH_KEY in the “Secrets” section. When everything’s set, you can trigger new deploy. Push some changes to the master branch, and GitHub will run the workflow, which will build the app, export it and let ssh-deploy deploy it. In this post, we explained how to deploy your Next.js application using Vercel, Heroku, and how to deploy it on a custom server using SSH.

Thumbnail Image of Tutorial Deploying Next.js Application on Vercel, Heroku, and a Custom Static Server