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.
Top comments (2)
When I first learned React I remember thinking to myself 'this is aweful' and 'why would anyone even bother writing React when there is vanilla javascript' 🤔😆
Thanks for the post though!
Haha! 😁