Get Started With Next.js

webdev, tutorial, codenewbie, nextjs

6 minute read

07/27/2022

When I first learned React I remember thinking to myself 'this is awesome' and 'why would anyone even bother writing vanilla javascript'. I kept feeling this way for awhile. React eventually felt like a massive chore to set up and use in a project. When I initially made my portfolio I made it with vanilla html and css with a few lines of javascript from some packages to make it more interesting. I didn't want to make a react app for something as simple as what my portfolio was at the time.

Then I started working on other projects for my portfolio, and writing more blog posts. I wanted to be able to more easily update both of these. Preferrably using API's and preferrably with multiple front end routes, which lead me to the decision to transition over to React. I had heard of Next, so I decided to look into learning it and used it to build my portfolio. Next once again makes me think to myself 'this is awesome' and 'why would anyone even bother writing normal React'. In this post I'm going to walk you through some of the features of Next, and explain how they're used in an actual app.

Installation

I'm going to assume you already have node installed. You'll also probably want to have a grasp on react and how to use it on its own before learning Next. To create a new Next app use

npx create-next-app

You'll then be prompted to name the app. If you'd like to use TypeScript with Next, add a --ts or --typescript flag to the npx command. That's basically it, you can install any other packages specific to whatever project you're working on, but you might be surprised by the time you're done reading this. You really don't need a lot with Next.

Frontend Routes

Everything in the /pages directory in your apps file structure is a route. For now, ignore the /api subdirectory inside of /pages. To create a frontend route just create a React component inside of /pages. In order to navigate between different pages use the Next Link component. You don't need to install react-router-dom with Next. Everything in _app.jsx handles the routing of the frontend. You don't need to import your modules, all you need is to use the Link in some sort of navbar component and let Next handle the rest for you.

You can also create dynamic routes pretty easily by creating a subdirectory in /pages. Just name the directory whatever you want the route to be. In the directory you'll need a index.jsx file as well as a file that indicates what value is going to be used to make the route dynamic. You wrap the value in brackets, so if you had a /blog route and wanted to have a route for each post with the id you'd create a file called [id].jsx. The file structure should look something like this

└─── Pages
│   └─── Blog
│   │   │ index.jsx
│   │   │ [id].jsx
│   │
│   │ _app.jsx
│   │ index.jsx

In your index file, you'll have to do a few different things. First you'll use more Link components to link to the dynamic routes. You'll also probably need to fetch data from an API in order to know what id's to use. Then each dynamic route will need to make it's own API call most likely. Luckily Next makes this easy while also letting you statically generate sites, or do some server side rendering.

getStaticProps

First, getStaticProps is just like it sounds. If you have data that isn't going to change on your app use should fetch it using getStaticProps and use Static Site Generation. When you have a function called getStaticProps it's automatically called when the page is requested and sets the props needed to render the page before it's generated so that the app can be statically generated. You can fetch data from external API's here, or just set the data equal to a value if you wanted. Here's what getStaticProps looks like

export async function getStaticProps() {
  // make request
  const res = await fetch("some url");
  // convert to json
  const data = await res.json();

  return {
    // return data in the props object
    props: {
      data,
    },
  };
}

This just goes in the same file as the React component you want to fetch the data for. So this is the bare minimum to know how to get id's for dynamic routing, next lets look at how to getStaticPaths() and how to statically generate the paths you'll need just like the props.

getStaticPaths

If you use dynamic routes and getStaticProps you'll need to set a list of paths to be generated. This is also automatically called like getStaticProps() and is called before the page is generated. Here's what getStaticPaths() looks like

export async function getStaticPaths() {
  return {
    paths: [{ params: { id: 1 } }, { params: { id: 2 } }],
    fallback: false,
  };
}

In the paths key that's returned it's a much better idea to map over some other data in order to create the array depending on the length of the data. I showed the hard coded way just so you could understand the syntax. Here's a better way to use getStaticPaths

export async function getStaticPaths() {
  // make request
  const res = await fetch("some url");
  // convert to json
  const data = await res.json();
  // map over data, adding each id to an object in the array
  const paths = data.map((post) => {
    return {
      params: {
        id: post.id.toString(),
      },
    };
  });

  return {
    // return paths
    paths,
    fallback: false,
  };
}

The getStaticPaths function goes in the [id].jsx file. You can then use getStaticProps to grab whatever data you need for that specific route. Before moving on to getServerSideProps here's a brief example of what index.jsx and [id].jsx might look like using both hooks.

// index.jsx
const Blog = ({ posts }) => {
  return (
    <div>
      <Head>
        <title> Blogs </title>
        <link rel="icon" href="/favicon.svg" />
      </Head>
      <main>{/* map over posts */}</main>
    </div>
  );
};

export default Blog;

export const getStaticProps = async () => {
  const res = await fetch("some url");
  const posts = await res.json();

  return {
    props: {
      posts,
    },
  };
};
// [id].jsx
const Post = ({ post }) => {
  return (
    <div>
      <Head>
        <title> Blog </title>
        <link rel="icon" href="/favicon.svg" />
      </Head>
      <main>{/* use post prop to render content */}</main>
    </div>
  );
};

export default Post;

export const getStaticPaths = async () => {
  const res = await fetch("some url");
  const posts = await res.json();

  const paths = posts.map((post) => {
    return {
      params: {
        id: post.id.toString(),
      },
    };
  });

  return {
    paths,
    fallback: false,
  };
};

export const getStaticProps = async (context) => {
  const id = context.params.id;
  const res = await fetch("some url" + id);
  const post = await res.json();

  return {
    props: {
      post,
    },
  };
};

getServerSideProps

I haven't had a reason to use getServerSideProps yet, but I'm going to do my best explaining how to use it. getServerSideProps only runs on the server, and is run at request time to get props from the server. It looks just like getStaticProps in that you still make a request, parse to JSON and then return props with your data inside. The main difference is when and how the data is fetched and rendered. If you'd like more information on the difference between Static Site Generation and Server Side Rendering and when you might use both check out this blog post from Vercel.

Backend Routes

Next.js also allows us to really easily build out backend routes by creating files inside of /api in the pages directory. You can connect a database or use any node package you'd like and query the endpoint from your frontend. In the app I made I have a contact form that hits the /contact endpoint and sends an email to my email address using node mailer.

In my contact.js file which is in the /api directory I have a handler function that looks something like this

export default function handler(req, res) {
  const body = JSON.parse(req.body);

  // Some logic to send email

  res.status(200);
}

Then in my contact component I'm able to have a handleSubmit function that looks like this

const handleSubmit = (e) => {
  e.preventDefault();
  const data = {
    // key value pairs needed to send email
  };
  fetch("/api/contact", {
    method: "post",
    body: JSON.stringify(data),
  }).then((res) => {
    // handle response by clearing state etc.
  });
};

Conclusion

Next.js is the first real 'fullstack' framework I've learned. I've used Rails and React to build fullstack apps but building apps that way never felt good. Even using React inside a /client directory in a Rails app just never felt 100% right. Next feels good to use. Next makes everything simple, but it might be overkill for your app. I would say that unless you have a need for the API routes and/or use hooks like getStaticProps() alot you might be better off just using React or another library like Vue or Svelte to build your app. If you're building any medium to large size project I would recommend giving it a shot though.

If you have any questions please leave a comment over on dev.to

Built with Next.js, Tailwind and Vercel