We'll create individual pages for each product using dynamic routes and statically generated pages. We'll do this by taking advantage of Next.js's ability to create dynamic routes and the use of getStaticPaths
as well as the getStaticProps
functions to create a unique page for each product.
To start we'll update the product page to a dynamic route using the bracket notion in our filesystem ([param])
. Import products JSON document and create static paths using the getStaticPaths
function. Proceed to configure static product props using path parameter with the getStaticProps
function. And finally, use the global cart state to add products to the shopping cart.
Lecturer: [0:00] What if we wanted to give a new page to each of our products to view only that product? To get started, inside of our code, I created a new static page called productId, where if we open this page up in our browser, we can see that it has a static URL and very static information. Static in fact, I'm defining all those values as constants at the top of the page.
[0:17] Using this productId page as an example, we know that if we create this file inside of our page's directory, that file path is going to make available as a route inside of our application, but we don't want to have to define a new page and route for every single one of our products. Instead, Next.js offers dynamic routes, which we can use to dynamically create paths and pages for our application.
[0:38] In their example, they're using PID as an argument, where if they wrap that argument in brackets, that's going to now be available as a router.query inside the application.
[0:48] To start, inside of our code, we can rename this file and we can add those brackets right around side our productId.
[0:55] If we go to our page and we change that value to anything such as Colby, we can see that it still loads because it's a dynamic value. If we look back at the documentation, we can see that, if we want, we can use the useRouter hook, where we can access the ID right inside of our component.
[1:08] However, that's going to make it only available client-side. When we're building our application, we want to render all those routes into static files. Instead, we're going to add an additional two Next.js APIs in order to first get the static paths for our application, and then pass in those props into each page that gets created.
[1:27] This is going to allow us to render each page as a new static file, but also taking the argument from the URL to find our productId. To start, at the top of our dynamic product page, we're going to import our products from two levels up, our products.json file.
[1:42] At the bottom of the file, we're going to export a new async function called getStaticPaths, where we're ultimately going to return an object with an array of paths along with a required property called fallback, which we're going to set to false.
[1:54] For our paths array, we're going to want to create a new path for every single product we want to create a page for. To do that, we're going to create a new constant called paths.
[2:02] For its value, we're going to take our products array and we're going to map through it, where we'll create a new function, where we'll have access to each product inside that array, where we'll return a new object with a property of params, which is equal to a new object, where we're going to define a productId that we get from our products ID.
[2:19] With our paths constant, we can update the empty array so that it passes in paths. At this point, if we reload the page, we can see that we're getting an error.
[2:27] Currently, we're defining our getStaticPaths function, but we're not defining a getStaticProps function. What's happening is Next.js is trying to go through all of our paths and create a new page, but it doesn't have access to the getStaticProps function which it needs to get the data for that page.
[2:42] We're going to add a new function to our file, and we're going to export an async function called getStaticProps, where we're going to destructure params from the first argument. We're ultimately going to return a new object, where we set a new property of props, where we will ultimately define our product. To quickly test this out, I'm going to console.log out our params.
[3:02] If we go to our products.json file, and we grab one of the IDs, we can go to our browser and we can go to /products/that ID, where we still see that static product page. If we look at our terminal, because getStaticProps runs inside of node, we can see that it's console logging out our params, where we have our product ID.
[3:19] Now we're going to use our products array similar to before, but now we're going to look for our product by its ID. I'm going to create a new constant called product. I'm going to use the products array to find where my argument will be its destructured ID. Inside that function, I'm going to look for where my ID is equal to my params' product ID.
[3:38] With our new product object, we're going to get rid of this empty object and pass product right through. What we did is we just made our product available as a prop to our product page. In our page component, we're going to destructure that product prop where we can console log out that product prop.
[3:53] If we reload the page, and we look inside our terminal, we can open that object and we now see our product. Now we can destructure all these same properties right from our product. I'm going to destructure my ID, title, description, image, and price from my product. I'm also going to get rid of all these static values.
[4:12] Now if we look back in our browser, we can see that it's updated with all of our product data. If we go back to products.json and we grab the ID of another item, and inside of our browser, we go to that path, we can see that we have the data for that item.
[4:24] This works great, but if we try to hit the Buy button that's not actually doing anything, but we made our cart state global, meaning we should be able to find that addToCart function and add it to this page. Inside of my product page, I'm going to import our useCart hook from two levels up, hooks useCart.js, and then destructure our addToCart function from our useCart hook.
[4:48] Anytime our button is clicked, I'm going to pass in a new function where we're going to fire addToCart, and we're going to pass in that ID as an argument. Now if we reload the page and try to hit the Buy button, we can see that it updated our global state. Finally, we have all of our product pass, but we don't currently have a way to access it.
[5:04] Currently, if we were on our home page we can only add to cart. If we try to click one of our products, it's currently not doing anything. Inside of our home page, we're currently defining an anchor tag. This is great for external links, but since we're using Next.js, we want to make a client-side route change.
[5:19] To do that, we first want to import a link from next/link. For now, we're going to wrap that anchor tag with that link component, where instead of applying the href on our anchor tag, we're going to apply that to our link component.
[5:32] We're going to change that href to a dynamic value, where we're going to specify products, and we're going to dynamically pass in our ID.
[5:39] If we go back to our home page, and we try to click on one of our products, we get taken there as a dynamic client-side route.
[5:45] In review, we had our products on our home page, but we wanted a way to view each product individually. To do that, we defined a dynamic route, where we were able to grab the product's ID as a parameter for every page created. To define those paths, we added a getStaticPaths function, where we loop through our products to create each path.
[6:02] We then used getStaticProps to find that product ID and return that product as a prop. Then we had our product data as a prop, where we were able to fill in our static page with dynamic data. We even imported our useCart hook, where we were able to add our product to the global state.
[6:16] After updating the home page to use the client-side link component, we were able to navigate to our products from the home page, see all of its product data from a dynamic route, and even add that item to cart.
Until now, I know how to use getStaticPaths & getStaticProps. Thanks Mate ~~ @dameng no problem, glad it helped!
I've followed the videos and coded along and they've been awesome. On video 13, I see that you added a new folder and file as '/pages/products/productId' and loaded the file content in the browser. But I did the same and typed http://localhost:3000/products/productId in my browser, I get a 'page not found error'. Did some troubleshooting but no success. Can you please show me how to resolve this and move forward. Thanks.
I've followed the videos and coded along and they've been awesome. On video 13, I see that you added a new folder and file as '/pages/products/productId' and loaded the file content in the browser. But I did the same and typed http://localhost:3000/products/productId in my browser, I get a 'page not found error'. Did some troubleshooting but no success. Can you please show me how to resolve this and move forward. Thanks.
hey there, do you happen to have the code that I can look at on GitHub, Code Sandbox, or something similar? the Next.js filesystem routing should definitely work! just looking at what you added as "new file", did you include a .js
at the end?
Hello, I'm getting a 404 when trying to get to http://localhost:3000/products/productId. I'm also not getting anything in the terminal when trying to console.log within getStaticProps() or getStaticPaths().
Oops! Just kidding, I figured it out. I accidentally placed my products directory in api/ instead of pages/.
Until now, I know how to use getStaticPaths & getStaticProps. Thanks Mate ~~