Intermediate Gatsby, v2

Client-Only Routes & Dynamic Pages

Jason Lengstorf

Jason Lengstorf

Learn With Jason
Intermediate Gatsby, v2

Check out a free preview of the full Intermediate Gatsby, v2 course

The "Client-Only Routes & Dynamic Pages" Lesson is part of the full, Intermediate Gatsby, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Jason live codes building a search component with dynamically generated content, a catch-all dynamic route, and a client-only fallback route. A catch-all dynamic route will cause every URL that starts with the folder name to be routed to this component.


Transcript from the "Client-Only Routes & Dynamic Pages" Lesson

>> When you're building for Gatsby by default, the way that Gatsby works, is to put all of the pages both server side rendered and client side rendered. So you start by building the side down the static files, so that if JavaScript is disabled, you can still load the page.

When the component hits the browser and rehydrate's then, like JavaScript fires, mounts the page, builds a react version of it and you end up with a fully functional react app on top of the static route. In client-only rendering, what you end up with is you run into these situations where let's say, for example, you wanna build a search page.

So your search results page cannot exist without some kind of input from the user. We can't, it's completely impractical to try to guess all of the possible search combinations that could exist and build that ahead of time. So instead what we do is we wanna build what's called a client-only route, where there is no server side rendering step.

We're instead going to say hey Gatsby, only put this file out there, so that all of the logic happens only when we get to the client. So if you load this with JavaScript disabled, it's gonna be an empty page. So with that being said, what I wanna do is let's actually build ourselves a search component.

And the way I'm gonna do that is let's start by giving it a few styles. So we're going to do a search.module.css. And this is because we're gonna have a search field, search button, and like a clear reset button. And I wanna make sure that those aren't just like, we're gonna do a little bit of styling on it, so we'll have a form in that will make that display flex.

I am a big fan of display flex if you haven't already picked up on that, and we can go space between, so that they kind of equally distributed across the page. We'll give them a gap of one REM, for the search input I want this to be font size of one REM.

So a thing that is a little bit frustrating and a little bit confusing about inputs is by default, they don't respect the document font size. So by doing this font size one REM the font size and the input will actually be the same as the font size for the rest of the CSS.

And another little fun fact is if you've ever been on a website and you tap into a search bar or to enter your email and the page zooms in, that's because the font is smaller than 16 pixels. So if you want to avoid your website zooming in when people touch your inputs on a browser, make the font bigger and it will stop doing that.

So this is to make this will prevent that from happening effectively. Then we're gonna set padding, on point two five on the top and bottom, so that it's got a little bit of breathing room, and one on the left and right to give it some input like shape.

Then we'll set the width to be 100%, and I want that to basically occupy all the available space that the buttons aren't using. Then for the buttons, we're gonna do a font size of 1rem, and we'll do just the same padding, so let's do 0.25 and 1, and I think that's good enough, right?

So then let's go in and let's make our first page. So I wanna create, so the way that Gatsby works is we looked at these file-based routes here. Gatsby also supports something called dynamic routes. So if we create, let's say, a search folder. And in this I wanted to make everything that hits that search route hit, like the same component.

We can do what's called a catch-all, which is the brackets and three dots and then .js, this is is very cool. It's very it's very helpful and it gives us the ability to say If you hit search, go ahead and just bring everything at this sub route into this page and we'll do stuff with it on the client side.

So in here we are going to write a react component as usual, import react from react. And then I also wanna get the navigate library out of Gatsby. And what this navigate library does is it lets us do programmatic. So the same thing that link does, where it when you click it, it takes you to another page and all those things.

Navigate lets us do that but programmatically, then I'm gonna get my style, so my form, my input, and my button. And I don't know why it came out of that, let's get it out of styles. Got to go up another level because we're in the search folder here we're going to get styles and search.module.css, then we can export default function We'll call it search client only, and this gives us params.

So what params is, is whatever is in the URL, and it gets broken up by slashes, so if we have one thing. It'll show us like a little well, let's just look at it and actually it's easier to show than to explain. So what we'll do is we'll get query, actually let's start by just doing this let's console .log params.

And then let's make it visual, let's do return pre JSON stringify params Okay, so this will give us the ability to hit anything in the search, so let's look, we got started, let's start the server. Now that this has started, when we look at the search we can see that because we used a catch-all route.

It gives us this star, and that star is like everything that's in here. And so we can put whatever we want in here some more staff. And it'll just feed us that path, whatever we put in here. And so, if we were to do something like our search query, like we wanted to search for, I don't know, The Hobbit.

It just gives us the search text right there, so we can actually build a search form like that. It's all coming right through the page, so let's do that. Let's build ourselves a search form. So what I'm gonna do is I want to in here, get our query, and that'll be, we're going to decode the URI components because we don't want any like percent 20s or things like that.

We will get whatever comes out of params star. And then we want to use some react stuff, so we're gonna store the current query. And then we'll have the ability to set the current query. And that's gonna be react use state. And we'll start with the whatever the query is, then we're gonna keep our results.

So we're gonna do a search we want to hold on to the search result. So have the ability to set the results, and that's going to be in react, use state. And we'll set that to null by default, and then we want to do a status set status.

And this, I hope that David Corti who wrote x date is not watching because he's gonna send me angry email but I'm gonna use a kind of a Status flag, what are we doing? So then in here, I'm going to add a couple functions. So the first one is, we wanna be able to handle the search.

And so when we get a search, it's gonna be as form submission. And so the first thing that we wanna do is prevent that form from actually submitting. We instead want to grab that here. So we're gonna prevent default. That'll prevent the form from actually submitting in one set, let us deal with it.

Then I wanna get the form data, which will be using the built in browser form data API. If you've never used this, this is something that when I learned about it as soon as Hinton pointed it out to me, I felt like a dang superhero. I had been doing this the hard way prior to this and wow is this easier.

So I can actually just get whatever the name of the field is. So if I've got my field down here, let's set these up so we can kinda look at what happens. If I've got a form, and the way that that form is gonna work, I'm not gonna fully mark it up yet.

But if we've got an end input of a type search and we give it a name of search, then what I can do up here is do a form.getSearch, and I'll get whatever the value of this is. It's so easy, it's built into the browser. Yeah, form management used to scare me and now I don't mind it, I kinda like it.

Then once we've got that, we can set the current query to be whatever the query is. And then I'm also going to navigate the browser. I wanna send us to search and we will encode the URI component for queries, so that if there's URL characters or any special characters in the string, it won't break our URL.

And once we've navigated, that'll be the end of how we handle our search. So, the other thing that we wanna handle is just a reset. So let's create a function called handleSearch reset. That is going to just set the current query to nothing, make it blank. And then we'll navigate back to search.

Okay, so that's that function. And the last one I'm not gonna write just yet, let's stub it out though. So it's gonna be a function called bookSearch. It's gonna take a query. And that one is going to, Look up a book. So the other thing, we're gonna do is, we wanna Watch for whenever the current query changes, so we're gonna use effect for that.

And the way that use effect works is we pass in an array of what we want to track. So we're gonna pass in current query. And then we can do stuff whenever the current query happens. So what I wanna do, is if the current query, Is empty, we're just gonna set the result to null.

And we'll just return, will be done, there's nothing else that we wanna do there. Otherwise if the current query is not empty, we gonna run this book search and pass it in. So this is what's actually gonna run our search. Whenever we update the current query here, this will cause the search to run.

And then in here is where we'll set the result so that we can show it on the screen. So let's build out our our basic form. So let's tell people where they are. We'll say search for a book. And so our form is gonna have class name of form.

And on submit, we're gonna run that handleSearch. Inside of that, we have the input, and we'll give this a class name of input. Then we'll do a button. That button will have a class name of button. The text on it will be search, and that's really all that buttons gonna do.

So, let's duplicate that one, and this one's gonna be different. This is gonna be a type reset button. And this will say, what's it gonna say? We'll call it reset and then let's also give it an unclick, Of handling the search reset. So effectively what we're doing here is we're saying, in this search form, lets look what's it's looks like, form is not undefined.

What are they? There we go. So now we've got this this search and so what a reset type is if I say some value if I reset it clears the form. And this is also built into a search form. So there's, ways to do that. And then what we should see here is we won't get any results yet but if I search we should see it update.

There it is, we get the hobbit up in the search bar. So pretty sweet, pretty helpful very useful. And the next thing for us to do is to actually write this book query. So let's do that. And the way that we're gonna do that is we're gonna use that Open Library Search again.

So the first thing I wanna do is I'm gonna set the status of our document to loading. So this is kinda very bad state management. Then we're gonna do a response which will be a waiting fetch and I'm gonna HTTPS. We wanna hit open, and we're gonna hit the search.Jason endpoint.

And this works with just a q and then whatever our query is, so we can pass in the query and just like that. Let me make that a little bit bigger so we can actually see what's going on. So we're gonna fetch that. And then after we have fetched that, we'll check down here if the rez is not okay.

So if something went wrong with the query, we will just throw a new error because that would mean that something went wrong with the search. And so it's okay to fail if that's the case, we probably want more graceful error handling but we're not going for perfect, we're going for learning today.

Okay, so if we get past here, we know that we've got a good result. So let's get our results, it'll be a wait, rest Jason, cuz we know we're getting back Jason cuz we call the Jason endpoint. And then we can set our results to result and we can set our status to idle.

And so effectively what we've done now, is we've created a little poorly managed state machine that will say kinda where things are. And down here, what we can do is we can start to respond to that in the UI a little bit. So what I'm gonna do is if our status is loading, we wanna show something like loading Results, right, and then if our status is idle and and the current query is not, as all tryna push, is not empty, then we want to show results.

Otherwise, we're just gonna show no. So let's get these results shown here we'll say dot H2, say search results for current query and then down here, we'll just set them up as an unordered list. Make sure that we've got a result because it's always possible that it comes back empty, and then we'll go through the result docs.

So what comes back from the search is metadata and then docs is the actual like, list of results, we can map those get to doc, and inside here we can set up a LI key. So doc, doc key that's a unique field in there, and I'm just I don't want to teach how the open Library API works, I'm kind of just flying through what the fields are.

What we could do is like run this in the browser and look at the fields and then kind of reverse engineer it, but for the sake of not getting too lost in the weeds. I'm trying to make sure that we cover this as quickly as possible, so we've got the strong tag here that's going to show the title and I want that to be the doc title.

And then I'm going to put in who the doc author is if the author is set, so I'm going to say doc author name, which is, which is going to be an array actually, oddly enough, being singular. And then we'll sit so we'll say doc dot author name and then I want to optionally chain this because it might be set and still empty.

So just trying to guard against unexpected KOs here, so let's save that, and this should work, right, let's give it a try, so, hey look, it's already doing it. So we've searched for the hobbit, let's search for another book, ready player one, there we go, we've got results, let's search for just just one more, what's another book?

I've only read like two books apparently, another book that I've read that I liked was Zen and the art of motorcycle maintenance. And there it is, so this is cool, right, at and we can make this more useful. We can link to a doc page, we could figure out a way to hook this ISBN up to another page where we could get more information but I'm not worried about that.

I just kind of want to show how you would set up these client only pages. And as you can see like this is not a particularly simple component but the nice thing about it is that everything that's happening in here with the exception of these navigate calls. These are all being done as it's just react, the only Gatsby specific thing we're pulling in here is this navigate function.

And the fact that this file is named something funky to make Gatsby pick it up wherever, the other thing that's cool about this is, this is also a. Like it's URL navigable so if I visit a page like I wanna come see the hobbit, it'll load the hobbit for me, so I didn't like pull it out and put it in the in the query but so you could share it First set of search results with somebody.

And so this is nice, right, it's kind of a stateful transfer of information if you store the query in the URL like that.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now