Build a Custom Storefront on Shopify with Hydrogen

Bookmark

Hydrogen is an opinionated React framework and SDK for building fast, custom storefronts powered Shopify. Hydrogen embraces React Server Components and makes use of Vite and Tailwind CSS. In this workshop participants will get a first look at Hydrogen, learn how and when to use it, all while building a fully functional custom storefront with the Hydrogen team themselves.



Transcription


Welcome to today's workshop. We're going to be building a custom storefront on shopify with hydrogen. I'm Catherine. My teammate from hydrogen, Matt, is also in the chat and he'll be presenting this workshop with me. Just to get some administrative stuff out of the way, we do have a chat here in the corner in the Zoom webinar, but we'd ask you to just move over to Discord if you have any questions because we have some team members from the hydrogen team in there ready to answer your questions. We have Josh Larson, Helen Lynn, and we also have our teammate, Michelle, who will be joining a little bit later. Oh wait, nope, Michelle's there right now. So cool. Matt and I will monitor it throughout the workshop and answer any questions that you might have there as well. And so this workshop is three hours long. It's a long one. We have a lot of stuff to get through. We're going to start the workshop by doing a high level overview of hydrogen and presenting some slides. It's sort of just going to be more of a little bit of a talk format, encourage you to ask questions again in Discord during that time. And after that, we're going to jump into coding directly and we're going to be sort of splitting the workshop up. Matt will do some coding, I'll do some coding, and we're going to do some breaks in between just because, you know, three hours. Let's all stretch our legs a little bit and ask questions and relax. We'll be using stack blitz for a lot of the coding in the workshop. It's been a little bit buggy for us this past week while we've been practicing, so we might actually end up switching to our local environment to do some stuff. But I'll give more on that later before we actually dive into everything. So yeah, let's get started. This is exciting. How would I pass it over to you, Matt? Matt, why don't you introduce yourself? Yeah, sure. So my name's Matt. I've been at shopify for four and a half years, doing a bunch of different stuff throughout that time, everything from some lot of Fed work, but also some production engineering. I live in Berlin, and we, my family and I relocated here when I took on a role at shopify to be on the global resiliency team. To be a part of incident response and kind of as part of expanding our global team was moved here to Berlin, but then what was supposed to be just a short stint ended up becoming us really liking Berlin and wanting to move here indefinitely. So but I'm originally from Canada. And when I'm not working, I enjoy camping in Ontario provincial parks. And I also feel as though I need to assert my preference as a cat person, because most of the hydrogen team is dog people, Catherine included. So I'll pass it back to Catherine. All right, cool. So hi, everyone. Again, this is me. I'm Catherine. I'm coming at you from Canada, specifically, I'm in Montreal. So I'm five hours behind all of you. It's around lunchtime for me. I've been working at shopify for the past three years now as a front end developer. And most of my time here has been spent on shopify checkout. So hydrogen is actually new to me. I started working on hydrogen at the beginning of this year. And I've had the unique opportunity along with my teammate Josh, who's in the discord to have worked on hydrogen since the very beginning. So this is really an exciting day for me. I'm so super excited to share what we have with everybody. I don't have much of a social media presence. You can follow me on Twitter. Here's my handle. Disclaimer, I only got Twitter like three weeks ago. So I'm still getting used to things. So please be my follower. That would be great. You can also follow. Sorry, I should I should also mention my non social media presence as well. You can find me at Cardiogram on most places, but don't don't expect me to be too active. But email me if you have questions after the workshop. My email is down at the bottom of these slides, which will be shared after. Whoops, let me just switch back. Here we go. And yeah, here's my email also, Katherine dot Griffiths at shopify.com. Please feel free to reach out, ask me about hydrogen. Happy to talk about hydrogen shopify front end development in general. And outside of work, you can find me walking my dog Kirk. Like Matt said, I am one of the hydrogen dog people. So this is my dog Kirk. He's a three year old Australian Shepherd corgi mix, and he's full of energy. So I'm out walking him a lot. But when I'm not walking him and in the wintertime, you can find me enjoying the very Canadian pastime of curling super fun. So let's talk about goals. Our workshop today is three hours. And during this time, we'll be building a custom storefront with hydrogen. By the end of the workshop, but you'll have an understanding of react server components and how and when to build them. You'll be familiar with hydrogen components, hooks and utilities. You'll know how to create new pages in a hydrogen app and how to make custom page responses to, for example, return PDFs or your own APIs. And lastly, we're going to show you how to deploy your hydrogen app so you can actually host your storefront and start making sales right away if you want to right after this workshop. So why don't we get started? What exactly is hydrogen? You're probably wondering. hydrogen is a web development framework optimized for building custom storefronts powered by shopify. It was introduced publicly at shopify's developer conference, Unite, back in June of this year. And here we have a screenshot of our CEO, Toby Luque demoing hydrogen at the end of the conference. He actually spent about 10 to 15 minutes building a snowboard store using all of the tools. If you haven't had a chance to watch it yet, I highly recommend it either at some point during a break in the workshop or even after the workshop, because let's be honest, it's pretty cool to see the CEO of a company coding something live. hydrogen's home on the internet right now is shopify.dev slash hydrogen. You can sign up there for updates about it to get put on our mailing list. And I highly recommend you get added to that mailing list so you can always be up to date on what's new. I'm going to add a disclaimer here that hydrogen is currently in closed beta with a few select merchants and partners at shopify. We'll be launching the developer preview later this year, so keep your eyes peeled. And like I said, sign up for that mailing list on shopify.dev slash hydrogen. The developer preview will give developers an opportunity to play around with the framework, give us direct feedback about it, about what works, what doesn't work and things they'd like to see in it, maybe things they don't want to see in it. And in this workshop, you're getting a lucky rare first look at hydrogen. So if you have any feedback about the framework, we definitely recommend that you post it in our Discord channel or reach out to Matt and myself via email or on Twitter. Now, it's really important that I emphasize here that we want to make you want to build a framework that developers love. We want developers to love using hydrogen, we want them to recommend using hydrogen to all of their developer friends. And your feedback for us to accomplish this goal is really crucial. So please let us know everything you think about it. You may be wondering, okay, hydrogen is a web development framework, but what exactly is it going to let me do as a developer? Well, it lets you build a custom storefront. But we also want to emphasize three other key things that hydrogen will empower you to do. hydrogen lets you build unique storefronts faster. It helps you skip setup to focus on building. And it helps you stand out creatively by doing these things. So when it comes to building unique storefronts faster, we've already made the decision for you to use new web technologies in this framework, such as vite and react server components. Our architecture enables a mix of static and dynamic data fetching between client and server for optimized performance. And hydrogen is intentionally designed to be fast and stay fast. So you're going to be building your storefront really, really quickly. It's going to be really, really fast. And your storefront itself is going to be really fast. Win, win. When we talk about setup and building and focusing on building, hydrogen comes with a collection of components, hooks, and utilities that take care of a lot of heavy lifting for you while you're building your storefront. You can bring in tools and libraries that you already love and are familiar with. And you don't need to focus on any boilerplate code. Let hydrogen take care of that. You can spend all your time focusing on designing and building something that's really cool and unique. And lastly, when we talk about standing out creatively, it sort of comes back to what I just described. hydrogen comes with so much batteries included that you can really let your creative juices flow and focus on building. And hydrogen is going to just take care of the rest for you. Our CEO mentioned in his demo at Unite that programming should be fun and that fun is a perfectly good reason for choosing a tool. And we want hydrogen to be fun. We want development with hydrogen to be fun. So you can spend your time focusing on building highly unique and memorable custom experiences on your storefront. And hydrogen will take care of all the rest for you. I want you to think of this as us trying to raise the floor on all the areas that we can at shopify so that you can more easily raise the ceiling on the types of creative and custom experiences that you want to develop and deliver for your users. Now I'm going to pass it on to Matt and he's going to talk to you a bit about our framework and react server components. Take it away, Matt. So again, I want to reiterate that this is bleeding edge technology. react server components are still in beta and very much considered experimental. So we're just right ahead of the curve, but things might change and they probably will. So in this workshop, we'd only like to provide sort of a higher level overview of how they work within the hydrogen specifically, but we'll provide a link to the react team's official RFC where they introduce them and you can follow along with that as the development continues. So that RFC was released with a video back in December 2020 where Dan Ibranoff and Lauren Tan identified the inherent trade-offs between good, cheap, and fast when developing user experiences. They illustrate many of the ways we are forced to juggle these, but how we can reduce the trade-offs by sufficiently taking advantage of the server. And this is where react server components come in. But despite the common word server in the name, they have very little to do with server-side rendering. Server-side rendering is when you take your client-side application, run it on the server so that you can render it into the initial html and return that while the rest of the javascript bundle downloads and bootstops and kind of sets up your whole react tree. We do this because we can generate html really quickly on the server and give our application something to serve just in the meantime while the react tree kind of boots up. But there still may be additional server-side requests later down in the tree where you communicate back to the server and this waterfall can become slow and often, and this is one of the main things that react server components were introduced to prevent. So with react server components, we just basically take some of our react tree and move it onto the server where you have the database a lot closer. And these components actually never make their way down to the client. So this includes the third-party libraries that they use or anything else that they kind of interact with. They'll never actually be a part of your client-side bundle. And instead, well, actually, this is kind of where that idea of zero bundle size comes from, that they've kind of coined alongside react server components. So what you do end up within the client is a streamed serialized response of your tree that then is used to rehydrate your client-side app. So however great this sounds, there are some constraints and rules that you need to be aware of. We've kind of got a diagram here to illustrate some of them, but we'll get a bit more into these when we start digging into the code. In short, your server components end with a .server.jsx postfix, and your client components are going to end with a .client postfix. And the ones on the client can operate only on where you can put your interactivity. That's where they can use client-side features, whereas on the server, you can't have any interactivity, and you can only use server-side features but not client-side features. And again, this will be a little bit more clear as we get into code. So these constraints may seem a little bit awkward at first, but we hope to introduce some tooling down the road to kind of help ease the transition. And if you want to learn a little more about that, you can check out my lightning talk on Monday, where I talk a little bit about the ES1 package and CLI that we're going to be releasing alongside hydrogen to kind of make this a little bit easier to kind of prevent anything from, yeah, just basically make it easier for you to adopt react server components, because there are definitely some new things to the api that may seem a little awkward at first. So that's sort of a bit of a higher-level overview of react server components. And with that, I'm going to pass it back to Catherine to talk about the various components that we're going to release with hydrogen. Great. Thanks, Matt. So hydrogen includes a large set of components for accelerating your development process while you're building your store. At the base level, we have, for lack of a better term right now, some primitive components. And these primitive components consume common objects in the storefront api. So for example, the image component in hydrogen consumes an image object that comes from the storefront api, and then it outputs an image tag. The video component, for example, consumes a video object and outputs a video tag. We also have more complex components, which set up context and have wrappers around these primitive components here. So for example, we have a product component, which takes and consumes a product object from the storefront api. This product object can include things like your title, your description, your prices, and all of the different variants that are associated with the product. And then we then have components. So for example, like product price, product metafield, product description, and they use the product context to retrieve that information and then just transfer it through to the primitive component and then render on the page. We also have a cart component, which consumes a cart object from the storefront api, which includes, for example, your cart merchandise lines, your estimated costs, etc. And it sets up a cart context within your application. And then we have components like the cart checkout button and the cart line component that make use of the cart context under the hood. So it's a handy abstraction for you when it comes to these complex components. I want to emphasize that hydrogen components are bespoke to shopify. Specifically, the data types that they expect as props align with the types found in the storefront api. This was decided on so that when you retrieve data from a query, you can just immediately pass it into a component. You don't need to do any extra steps of data processing. You just query and then pass the data along. Now this doesn't mean that hydrogen components are totally incompatible with non-shopify source data. If you want to use non-shopify source data, so for example, maybe you have content stored in insanity or Contentful, you can certainly use that information, that data. You just need to query it with one of our hooks and then you just need to do the extra step of just process it into the type expected by the components and pass it on. Now before we get too much deeper into component stuff, I want to take a moment to mention that, or to talk about headless components. And you might be familiar with this concept already if you use the headless UI library offered by Tailwind. So all hydrogen components are headless components. What do I mean by that? Well, headless components at their core, they reflect the separation of concerns between UI and business logic. So hydrogen components contain all of the business logic for the commerce concept they encompass and they leave all the customization related to styling and voice and tone to the developer. Our components provide sensible defaults out of the box in terms of which html tags they output and what default values they output to the page. But they also make it easy to diverge from these default values via customization with pass-through props and render props, which I'll get to shortly. When rendered by default without taking any class names or styles, hydrogen components will only take the styles that are provided natively by the browser. So another way to think about a hydrogen application or an application that uses hydrogen headless components is that a hydrogen storefront consisting of only hydrogen headless components will be functional and still render nicely even if you don't provide any styles on it. That's great. Now another thing to mention about commerce is that the default display for many commerce objects relies on the country and the language context in which it's used. So for example, let's take price. The price of a product, how it's displayed depends on the currency code as well as the language and the locale of the buyer that's viewing it. And as such, hydrogen components always ensure that they're outputting defaults in a localized manner when applicable. This involves under the hood, hydrogen components make use of many built-in javascript objects. So for example, international number format for displaying things like currency and measurements, as well as date for displaying dates and times. Now with hydrogen components, the idea here is that we want to make all the common use cases very, very easy, especially with our defaults. But we also want to make sure that uncommon use cases are still possible. And that's where customization via pass-through props and render props come into play. Developers should rarely, if ever, need to diverge from a hydrogen component and build one entirely by themselves because we're really aiming for these headless hydrogen components to be used for like 99.999% of use cases that you can think of. All right, now I mentioned in the last slide that hydrogen components are highly customizable. And one way we do that is with pass-through props. Pass-through props are props that we literally just pass through the component to the html tag that it uses under the hood. So let's look at, for example, the image component from hydrogen. By default, it takes a single prop called image, and this corresponds to the shopify storefront api image object. And it outputs an html image tag with the source and alt corresponding to the image's original source field and the image's alt text field. Now any attributes that you could pass to an image tag are also supported by the image component. And they're passed through exactly like you see here. So for example, if I want to include a class name, I would use the class name prop. If I want to include an onclick function, I would just use an onclick prop. The only props that you can't use for an image component, for example, are the source and the alt props, since the image tag makes use of these directly to ensure that the image displays properly and has the correct accessibility attributes right off the bat. Now in some cases, you can even customize which html tag is outputted. So for example, we have a component in hydrogen called the raw html, and that takes a string of html and renders by default a div with its inner html set to that html string. By default, we output a div, but there might be a case where you want to render this as a section, for example, instead of a div. And you can do that just by passing the string section through as the as prop. And when that happens, instead of using a div, we'll output it with a section. Many of our components include a graphql fragment that you can use to easily query for all the data you need for a component. So for example, a lot of these primitive components we were talking about, they make use of common objects in the storefront api. The image component, for example, includes an image fragment, which automatically queries the ID original source and alt text fields on that image object. The video component, for example, includes a video fragment, which queries for the preview image and the sources in that in that object. We'll discuss a bit more in detail about fragments as we actually go through the coding in the workshop. Along with a set of components, hydrogen also offers many hooks for you to use. And many of these hooks are actually used under the hood by the components I've just shown you. So for example, we have a hook called use shop query. And this allows you to run queries to the storefront api in your server components. We also have a hook called use product, which allows you to access product information in any components that are children of the product and product provider components. And we have a use cart hook and many other cart related hooks for callbacks that let you harness the power of cart. This would include things like adding items to cart, removing items, and even just checking out. And lastly, before we dive into things, I also want to mention that hydrogen also includes a variety of utilities that you can use to make your development easier. And all these utilities are used under the hood in hydrogen components and hooks already. We've just exported them to you because you might find them useful as well. One such utility is the flatten connection utility. And this utility, it takes a shopify storefront relay data object. And this is an object you can get for any sort of list type information in the storefront api. So for example, a list of media files, a list of product variants, or a list of meta fields. And it takes this list object and it just flattens it and transforms it into a flat array so that you can easily make use of the data that's in there. So I'm sure at this point you're all pretty eager for us to move on and do some coding. So let's just do a quick intro here to get started. This should be the last of the slides that you're actually going to be seeing. So give me a moment here. All right, here we go. So like I said earlier, we're planning to use stacklets for the workshop, but we noticed when prepping that sometimes it can be unstable. So in our demos, Matt and I will probably revert to using our local hosts. And while we do the coding, we're going to commit it to a branch and share the branch with you. So when we get to breaks, you can always pull down the branch that we've pushed and be right where we left off. Now it's up to you if you want to work with your local hosts or if you want to use stacklets, but I highly encourage you to use stacklets if it's working. And the reason for that is we can just guarantee that participants are all using the same environments. It's a little bit easier and it's a lot less prone to bugs in case there's differences between environments. That said, if you do decide to use your own local host machine, you can go ahead and pull this repo here, github.com slash shopify slash hydrogen workshop. We'll be working off of the main branch to start. You'll need to make sure in your local environment that you have node 14 plus installed. And you'll also need to have either yarn or npm because to start the repo, you'll need to run yarn or npm install. And then you'll need to run yarn dev or npm run dev to actually start up the local development server. So I'm going to go ahead now and I'm going to open up stacklets and just give you an idea and overview of what you can find there. So let's go take a look. Here we go. So it looks kind of like a visual studio code editor for those of you who use visual studio day to day. You'll notice just right down here in the corner, it does say web container is in beta. So this is where a bit of our instability happens every so often. You'll find over here, you have your list of files, your whole project structure. We have our terminal down here. We have our editor here. And then over here on the right, we have our actual browser window. Now there's a bit of a state right now where you just have to refresh a few times to get everything to start working. Like I said, web containers are currently in beta. So this is a little bit of a buggy part. There we go. There are a few refreshes. We see our first little Hello react advanced London. I'm going to go through now just the file structure to give you an idea. And actually, before I do that, this is something I put in my notes that I need to tell everyone. And of course, I totally forgot. You'll see up here in the left, there is this button for forking the stacklets repo. I recommend you do that now. We noticed while we were demoing and practicing this workshop that if you don't fork it and then you make changes and you save a file, stacklets will automatically fork and save for you, which produces a little bit of like a 10 second delay. Sometimes it's a bit of a pain in the butt. So go ahead and fork this right away if you can. I'll do it just to show you. I've just clicked forked and the project is forked and it's reloading. Let's take a look at the file structure here. And this is a file structure that should be familiar to a lot of you who are already building a lot of modern front end development apps or building react apps. We have our source folder here where we're going to be doing the majority of our coding. We have a components folder where we're going to be putting some client and server components that we built throughout the workshop. We also have a pages folder here. And the pages folder is where we're going to be adding server components. And any server components we add in this folder are automatically going to get registered as a route in our hydrogen app. So we're doing some nice file-based routing. It's cool. Next thing I'm going to show you is outside of the source folder. We have a docker file. This is important for deployment and Matt's going to be showing this at the end of the workshop. I'll open up our package JSON here to show you a bit of what we're using. So one thing to emphasize here in our dependencies, we have shopify hydrogen. We're using version 0.3.0. Like I said, we're in developer preview right now, hence the version zero. Sorry, we're in beta right now, hence the version zero. But once we reach development preview, we'll be up in version one. You'll notice here also that we're using react 18. So I just want to emphasize right now, react 18 is being used because it has several, a lot of functionality in it that we need for react server components. So hydrogen right now is actually using its own version of react server components based on the spec that the Facebook team has provided for it. And in order to build these react server components in hydrogen, we needed to make these react server components in hydrogen. We needed to make use of utilities that are only found in react 18. That's why we're using it today. If we go and look at the dev dependencies, you'll notice that, excuse me, we're using Tailwind css. Tailwind css is a really handy css library and actually we can go and visit it at tailwind.css.com. And it's a utility for css framework with packed with classes like it says here, like flex, pt4, pt meaning padding top four with its spacing, text center meaning align your text center, rotate something 90 degrees, et cetera, et cetera. We even have, let's say we want to look at text color, which is something that we might play with a little bit. We have really handy css classes here like text black, text white, text gray 50 for a certain state shade of gray. We're going to go more into Tailwind as we actually do the demo. So I'm going to keep it high level in that for now. And I'll also point out here in the package that we're using vite. So vite is our build tool and we find it is super fast, just like the name. So we're using it and it's great. Let's go ahead now and look at this super important file. This is the shopify config.js file. Now this is a file you need to have in your hydrogen store or hydrogen apps, excuse me. And you need to have it because it contains all of the information for hydrogen to communicate with your store via the shopify storefront api. So this includes the store domain information and the storefront token information. Now we've included here just a information for a demo store that we've built already for this workshop. It contains just a list of randomly generated products with images. And we encourage you, if you want, you can use our store. But if you've already built a store like we had in our setup instructions, then you can put the information for your store here and I'll show you how we can do that. You can go into your, excuse me, I'm just going to take a drink. All right. So you can go into your partner admin and you can log into your store that you've already created previously in your setup instructions. Let's wait for that to load. Great. So when it comes to the store domain, you're going to want to look up here in the URL. You'll see here we have react-advanced-hydrogen.myshopify.com and that corresponds to our store domain. You don't need to take the HTTPS or the slash admin. So just copy paste this section here in for your store and paste it right in here on the store domain. When it comes to the storefront token, we're going to need to go into a different section of our admin. So we'll go into apps because we're building a hydrogen app. We're working with a private app. So we're going to go down here to the bottom where it says working with a developer on your shop, manage private apps. So go ahead and click manage private apps. You're going to find the app associated with hydrogen. In this case, in the setup instructions, I think I told you to remember this name just so that you can easily find it, but this is the name you gave the app when you integrated hydrogen with it. So go ahead and click on that. And hydrogen, it's important to note, is using the storefront api. There are two types of APIs that shopify supports. It includes an admin api and a storefront api. But since we're building a storefront, we're going to use the storefront api. So once your page loads with your app information, scroll down past the admin api section and just go right to the storefront api section here. When you scroll down to the bottom, you're going to see this storefront access token. Go ahead and copy that and then paste that here in your storefront token. Now you don't have to worry about keeping the storefront token private. It can be publicly seen. So that's one of the reasons why we don't mind you using our react advanced hydrogen store. Now, before I pass it on and we do a short break, I'm going to show you in our source folder here. We have a file called appserver.jstacks that we've already built out for you for the workshop. It contains a lot of boilerplate code to get started, but I want to emphasize a few different things that hydrogen is providing out of the box here. So in your hydrogen app, you're always going to need a shopify server provider component. This provider makes sure that all of the children in this react tree have access to the storefront api. It makes sure it has access to everything that is available in the shopify config that I just showed you. It also under the hood just sets up everything so that we can have hooks that interact easily with the storefront api. So this is a really key component that you need for your hydrogen app, but it's already there for you in appserver.jsx. I'll also point out that we have, so we're using switch here. Switch is coming from react router, which might be familiar to a lot of you already, but I'll also highlight here that we have another component provided by hydrogen called default routes, and that's what sets up our file-based routing system. So any components we add in pages, the folder here, default routes is going to take that and just translate it into some routes automatically for us. Now with all that being said, we're going to take a short break now. Matt is going to take over after me to do some coding to start building our hydrogen storefront, but in the meantime, let's take five minutes. We'll be available in the chat to help everyone who needs to add their own storefront api tokens available. So I'll stop sharing, and we'll just take a short break, and we'll be back to start in five minutes. So on my watch that's be at like 12 43. you you you you you you you you you you you All right. Okay. I think we can probably move along with the coding portion, get our hands in a little bit wet with some code. So I'm going to run the app locally. So I've got this. Hopefully everyone can see my screen. I've got the application here on the left, and I've got the code window, or about the app. I've got the browser here on the right with our homepage, which right now just says Hello react advanced London. And the first thing that we're going to do is we're going to start by making our app look a little bit prettier. We're going to add sort of a frame component to it, which we're going to call layout. And we're going to dynamically get our shop name from sort of from our from the So the way we're going to do that is we so first things first, we're going to create a new components in our components folder. So just you're following along, just create a new file called layout dot server dot jss. And the reason that we're choosing a server component here is because this component has a data requirement and no interactivity. So this makes it the perfect candidate for a server component. So in here, we're just going to do our boilerplate, create a react react component. So we'll expert default function, we'll call layout, capital. And to start, we're just going to take a children prop and return. So you can destructure children from the props. Matt, do you mind just zooming in on the text in your editor? Sure, sure. Thanks. How's that? Perfect. Thanks. Okay, cool. So yeah, we have our component and we're going to use this inside of our homepage. So what we'll do is we'll import our new component. So import layout from we'll go up a directory and then scoot over to components and then layout dot server, and then close it off. And then we'll just wrap our app and layout. Make sure that you use the right combination of quotes. Great. So now that we've got a component that's just rendering children, what we can do over here is we know that we want to use the the storefront api. So we're going to use a hook that comes with hydrogen, an important one called U shop query. Just like that. U shop query. And this is going to come from app shop. And the way that U shop query hooks works is very similar to other data fetching hooks that you might be familiar with. So something like these query hooks from react apollo or react query. So we're just going to grab the data from the result of U shop query. And this is going to take an object with a query key and we're going to actually pass in a graph QL AST into this. So the way that we're going to grab that graph QL AST is you're going to import another really common library that you may have. They may be familiar if you've done a few similar libraries called graph QL tag. And this library is responsible for taking a graph QL query and turning it into the AST that we're going to pass into query as our query key. So let's create this query. And one way that we're going to do at least this first one is you're going to want to open up a new tab. And you can do this also in stacklets as well. You can just go to the, or you can just follow along. You don't need to necessarily do this. But a good thing to know is just that we ship the graphical editor with hydrogen and that mounts at a URL slash graph QL. You can also, I think it's graphical as well. And then you can also do, I think, underscore graph QL. All of these will load this graphical interface, which if you've not seen before, I'll just do a quick overview of how it works. There's basically two panes. On the left-hand side, you can write a query, which will remember your query from the last time you wrote it. But you can write a query here and then the resulting data will show up on the right-hand side. And it also provides this nice documentation explorer that you can toggle on the right to sort of inspect the api and see what this graphical api provides. So using this, we're going to construct our query that we're going to pass into our layout. So we'll start by just typing the word query. This is the main operation. If you don't give it an operation, you can also just open up curly braces and it'll default to using a query. But just to be proper, we'll give it a query operation. The other options here are mutation and subscription, which are just the primitive operations for graphical ideas. So we'll do query, open that. Actually, we'll give it a name too. Again, optional, but best practice. We'll call this shop name query, but you can give it whatever name you want. And then in here, this is where we're going to write the data that we want to get back from our api. So a little handy trick is that if you hold control and space, it'll autofill what it can from the fields that are available at this point where your cursor is. So here we know that we want to actually get a field from our shop. So we'll start with shop, open up another field set, hit control space again. And then we can choose what from our shop that we want. And in this case, we want to grab our name. So we'll hit play after we've done that. And you should see your name here, unless you're using, you know, you should see whatever name you're from your shop, if I can think that corresponds and it should come back in your response. You can add more fields here, but we're going to keep things like, because this is all we need for our layout component. So now we can just copy this, start our variable that we named over here, pass in the GQL. They're called a GQL template tag. And these are just backticks here. And inside of these backticks, we can put in our shop name. So now that we've done that, we can console log the result of this query and back over to our app. I'm just going to close it of graphical. You'll notice if you go to the console, refresh, there is no console log here, but if we open up our console log over in, spread out here, hopefully that's somewhat visible, but you can see that we do get the result here in our server logs. And that's because this is a server component running on the server. It never makes its way down to the browser and it never runs on the browser. So there would never be a console log here. So let's clean this up a little bit too. Just our windows a little bit. Great. So now that we have this, we can wrap our resulting component here in a div. And we can use this variable that we just got. So data.shop. And what we'll do just so it's very demoing this, we can just throw it in an H1 for now. Hit save and we get our, we get our name there at the top. So like we said, this is sort of the outer frame of our app. So we're going to use the Tailwind css framework to kind of give this a little bit more style. So later on, when we list products down here, we can have sort of a header component that is persistent across all the pages. So we can get rid of our console log here. And we'll start by wrapping our main, or sorry, children in a main tag, just to be a little more semantic. Give this a class name prop. And in here, you know, if you're familiar with Tailwind already, you may want, you know, you can just go to town with whatever classes you want, but just to kind of keep things simple, we'll just do the bare minimum. Give it a marginado, a max width, let's see, 7xl. And a little bit of padding on the left and right. So we'll give it a px, which is like a padding on the x-axis and we'll say 6. But you can tweak these to your preference. You can see it, you know, it kind of gave us some padding into one already. After you've done that, what we're going to do is start with our more header component up here. So let's start a header. And we'll get that a class name as well. And we'll give this some vertical spacing. So we'll do my of 10. And we'll center our texts. Also, if you're curious, I have a Tailwind VS Code plugin that is super helpful. And it kind of gives you the Tailwind autocomplete classes. So if like me, you're a little bit newer to Tailwind, it's awesome. Because, yeah, it makes life a little easier. Yeah, so we won't do too much here. We'll do that. And then we'll maybe add a little bit to our heading to make it look more like a logo. Let's say a font fold, for example, proper case. Maybe give it some tracking, some wider tracking. And let's make it maybe a little bit too. Sure. Looks great. So now, oh, we probably want this to be a link back to our homepage. So out of hydrogen, we can pull in a link component, which is going to basically be, it's basically a react spot. So we can pull in a link component, which is going to basically be, it's basically a react spot of the link component that comes with react Router, which Katherine mentioned we're using as part of our switch, and also it wraps our default routes. So we'll just use that. Wrap our logo text in it. And if you're familiar with react Router, it has a two prop, which we're just going to send back home. So this should be a link back home. And it is. Now to be, since we've got a little time here, let's be a little more semantic and let's add a skip to content link. So we'll throw skip to content inside of an anchor at the top. And again, using our class names that we get from Tail Link, it's really easy to do this. You can just say, when we focus it, we want it to be blocked. When we also want to focus it, we want it to be not screen readable. But by default, we want it to be screen readable only. And finally, we'll just wrap this in the div so that we can position it absolutely to the top of our, to the top of our frame. So we'll give this a position of absolutes. Let's say top, top is zero and left is zero. So this come, oh, and actually, sorry, one more thing. We'll pass it the right href, which then will correspond to the ID tag that we'll have here. And this just makes it so that when this is clicked, it'll jump down to the position that this is in our browser. So given this sort of class structure that we've got, we should be able to tab, see a skip to content link, press enter. And though we didn't get the scroll because our pages are long enough, there's not enough content, we did get the URL bar changing. So if this page was long enough, it would jump to our main content area. Just get being responsible web developers. And that's going to conclude kind of what we want to do with our layout. I mean, you're free to do more in the breaks or whatever. But for now, we'll just do something simple like that. So next, we want to get our products in here, get a list of products, get a grid of products. So we'll head back over to our index dot server. Just give me a second here to rearrange myself to get caught up. Okay. So what we want to do is we want to fetch our product list. Oh, you know what I'll do actually first, just in case anyone fell behind. I'll just quickly start a branch as Catherine mentioned. And so we'll call this life. Okay, that should be it should be available now if anyone fell behind and they just want to check it out. I'll try and remember to commit to that branch as we go. So we can see here, this is where we get our reactive advanced learning. You know, if I were to remove some, some text from here, it would remove from our page here. So this is where we want our product list component to come in. So let's create that. So we'll create a new component. We'll call it product. And in here, what we're going to do is we're going to export another URL, default react component dance here. And we are going to just thinking about the api here. I think what we're going to want is we're going to want to pass in a list of products. Because this is a client component, we actually want to do our, our, our fetch on the server. So we're going to do our, we're going to do a fetch for our products in the index dot server, and then render our product was passing it the products. So let's import now at the top of our index dot server, we're going to import the component we just created. So again, jump out over and product list. Good. And go here. And we know that we want to give it some products. So we need to, we need to get, we need to define this. So, you know, we want to do another fetch over to our graphql api, which means that we're going to import use shop query. So we're going to import use shop query. Flash hydrogen. And we'll use that hook. So constant data equals use shop query. And again, we need to define our query here. So cons query. Let's SQL, which don't forget to import this. We've pre-installed graphql tag, just so it would be in our package JSON. But if you're starting from scratch, just know that this doesn't actually come bundled with hydrogen. This is up to you to define queries however you want. So GQL. Great. So now what do we want to query? So what we're going to put in here is, this time we'll just write it without necessarily going to the graphql interface. I think you can do this one on our own, but feel free to use that if it's a little easier for you, if you want to see what else is available outside of what we grabbed. So we'll just give this another, like, so queries are operation, home query is the name that we're going to give it. And so inside of here, we can grab our products. And if you remember from Katherine's introduction, the products is going to return a relay object connection. So this is going to have edges and nodes. And what we're going to actually use, again, thinking back to Katherine's introduction, we actually provide out of the box with hydrogen, some fragments, so that we don't need to necessarily write all of the fields that we're going to want off of our node. So yeah, products, edges, node. This is where our product fields are going to come in. But what we're going to actually use in here is one of these fragments called product provider fragment. We're going to import that from hydrogen. And also the way that when you use a fragment, you also need to add it to your template literal here as an argument. So just that, and then it gets parsed into the ASC property. So let's just review a little bit here. We created a query using our graphql tag function, our template literal. We grab a product provider fragment, spread that out along the node, and also include it at the end. We created a product list, client-side component, put that inside of our components, just alongside our layout component. And then we're passing it products, but we still don't have products. So what we're going to do is provide the product provider fragment a bunch of variables that it's going to tell the storefront api how to return the result of this. So this is going to seem like a little bit verbose for our simple example, but in your actual shops, this would make a little bit more sense that you would want to specify a lot of these. So what we're going to do is pass in some variables into this product provider fragment. So let's define those. And you can do that by opening up almost in the same way that you would define a function in javascript. You can open up some parenthesis in the query. And in here, we're going to put in our graphql variables. So those are going to start with NumProductMedia. And then here we give it a type of int and it's required. And that's into the capital. And sorry, opposite depends here. We're going to do that. Yeah. Okay. So, and then this exclamation point just means it's required. So again, just, we're going to add a few of these. So NumProductMetaFields is the next one. And ints as well. These are all going to be ints. We're going to do NumProduct Variants as well as ints. NumProductSellingPlans. And just make sure you add the dollar sign for each of them. And this is SellingPlanGroups. And also ints. And see NumProductVariantsMetaFields. And that's the ints as well. I'm not seeing the type today, which doesn't make sense. And that's the last one. Thank goodness. All right. So now that we have all those, our product provider fragment will be happy. And we're going to actually need to provide all these variables as well. So we're just going to copy this whole block and pass it into the variables here. And this is going to be an object. And again, this is pretty similar to the way that a lot of graphql kind of hooks work that you may already be sort of familiar with. So the syntax here is a little bit different, but this is where we define the variables. So we don't need these dollar signs anymore. And what we want to actually do is provide the actual integers here. So for NumProductMedia, we'll do 10. We'll do 10 of the metafields. These are obviously going to be slightly different for your use case. So I think, yeah, there we go. And for that one. For our variance, we're going to actually do 250. For our selling plans, we'll do 0. So 0 for our playing peaks as well. And kind of the metafields and for our locations as well. But again, this is going to be, these would be more relevant in the situation when you have a more complex api. So we've got some errors. So I'm just going to see kind of what maybe I mistyped here. And see query variables. See what we got in that box here. It's just a syntax error. Oh, I think I'm just missing one of those. Yep. There we go. Not too bad. Not too bad. Okay. So now that we've got all this defined, we can, let's just actually, we'll kind of just comment this out for a sec. And again, just inspect where we get back from. Yeah, so console. Sorry, this one actually should be there. Good. Much better. Okay. Let's take a look at what we got in our response. So let's see. Oh, we've got to provide a first surprise. So again, just in the same way that we provided arguments up here in our variables, we're also going to provide them down here, but this one we're just going to put in line for now. We're going to come back to this, but let's just grab the first 10 of our products. Save that. Take a look back at the console. And you can see that we did get a, just ignore this cart failed to load for now, but you do see the data coming back from the server. And you can see that it is in this edges array. And if we were to stringify this as well, you would see that the actual full object is going to be inside of a node. And then we'll get our product provider fragment. But instead of doing that, what we're going to do is we're going to use the utility that Catherine introduced called flatten connection. Grab that. And now we're going to say cons products equals flatten connection. And we'll pass in the data dot products from our graphical response. And now let's take a look at that instead in our console. So when we refresh the page, you can see now that we're actually getting the fields out and that there we have to compare price range, media, meta-fields. These are all the things that can came in with our product provider fragment. And so what we'll do is we'll pass that directly into the component that we made before our product list component, remove our console. And this is very notes because why? Product list runs product list dot clients. It's definitely because of this practice, but let me just try this one further. Oh, that's why because we're not returning anything. You need to return no if you don't want to return something. Otherwise, you'll get that that we have here. Great. So in here now, we know that we're already getting products in here into our product list. So what we're going to do is we're just going to open a div and we'll iterate over those products. Let's do dot map in here, grab the products and we'll just return another day with the product. Close up our function, close up our interpolated variable here to start no JSX and then close it. So we should everything worked out. Should be getting products in here. Let's type something. Oh, yep, I did. So it should be products into as our problem. There we go. So we got a list of the titles of all of our first of our first 10 products. And though we're only putting the title of here, we actually have all of the data available to us at this point. So let's go back to our product list. Let's make this a grid for one thing. So super easy again with our tailwind classes. So we'll just say grid and we'll say grid calls. And you can, again, you can do whatever you want, but free calling sounds good to me. So we should now be in a grid. Excellent. So now we want to probably do quite a bit more than just our product title here. So we're going to start using some components from hydrogen. So import the product provider. So this corresponds to the product provider fragment that we're using in our query. We'll put that from shopify. And this is going to set up the context for our products. So we're going to wrap our individual product render function here, and we're going to add that and then pass it the product for each iteration. And we'll close off. And we'll actually just get rid of our product title here. Because now that we're using our product provider, we can start using some of the other handy components from hydrogen, such as product title, and just put that up there, render that inside of our product provider. Oh, and a little bit of an error on my part here. We're inside of a client component. So we need to dive into the hydrogen slash clients package. There we go. So we still got our product title, we're still in the grid, but now we're using the product provider. And there's a number of components that come that are product oriented. And then we'll see a bunch of them in this workshop, but another one would be like product description. So, you know, we could do this, although we don't really want to because in this view, we just want to do a grid, we don't want a product description there. But just to give you an idea of how easy it is once you have set up this context and this product provider here. The next thing we're going to do is, because like, to be honest, this whole grid thing is going to get a little bit complicated if we do everything in line, we're going to create a new component and we're going to call this product card. So new component, product card client, this is another client component. And we'll do our export defaults function. And then we're just going to take all of this jazz here, put it there, import our missing libraries, move those over to here as well. And then instead of what we were doing before, we're just going to now just pass it off to our product card, pass it the product for each iteration. And we can just self close that div and make sure you just import the product card. So it's right next door. So product card. Great. So we got a product card. And let's see, product is not defined, passing a product as our prop here. Let's make sure you can structure that there. And then that should do it. Good. Wait a sec. If anyone needs to catch up. And I'll just talk for a bit. The next thing that we're going to do is you're going to get some images in here and kind of make this look more like an actual shop, get the price information and so on. So this product provider, in addition to taking a product, we'll also take a first variance or an initial variance. So we can do that with the initial variant ID prop. And this will set up sort of like the other things that are variant specific when talking about the context within the product provider. And so to get the variant ID, we're going to make a new variable called first variant. And we're going to make that equal to product.edges. Actually, we'll get the first product, first edge. Sorry. And I just want to make sure I didn't miss anything with that. Yeah, I did. Variants.edges.node. So we could use our flying connection helper here, but in this case, we know we just want to grab the first one. So we'll just do that for now. And then we'll pass first the variance into here, the first variance and dot ID. Now we can actually use the selected variance image. Just very easily get that once we can put the components from hydrogen. So not only are we providing some components to output some product information, but we also get some handy components for variant image and so on. Let's see. Why are we not? Yeah, it's a selected variant instead of select variant image, just a typo in the name. Sorry. Thanks, Catherine. No problem. There we go. And we got some images. And they're in a grid. Great. Great. It's going pretty well, it seems. So we're going to put the image before the title, I think. And we'll also grab the price under here. And this is going to be the selected variant price. We'll input that from hydrogen as well. That should give us the price. And let's fix up our grid here. We'll wrap these in a div. And again, now we can apply a little bit of style here. We won't spend too long on this, but maybe give them some vertical padding or padding at least on the bottom. So we'll do maybe mv6 for margin bottom 6. And we can pass these class names directly into the hydrogen component. So here we can do font medium, maybe. Yeah, give them a little bit of boldness. Maybe dy of 2. Maybe we'll do something on a price name here too. So we'll just do x grade 900. Yeah. So whatever you want to add to your grid, you can do that. Finally, we're going to want these to actually go to somewhere when we click them, go to a product page. So let's add a link here as well. And we'll just wrap. And we're going to give that our 2 prop. It's going to be a slash. Open the code base and then we're going to do slash products. And then we want to grab the variable of products. And these will have a handle, which will be kind of like a slug that we want them to link to. And we will just import the link. So with that, now these will all link to a page that will be slash product slash Holywind slash Sprigran Hill and so on and so forth. Cool. So let's shoot back to index for a bit. Matt, do you mind just pushing these changes to the live branch? We have some people in Discord asking for it. Sure. Sure. Thanks. Let's give these a gap first so they're not budding up right next to each other. I think it's gap. Yeah. Yeah. And that'll kind of space them out a little better, I think. Yeah. And we'll do, um, just so it's a bit more responsive and right before I push this up, we're just going to preface the great calls with medium. And this is a tailwinds way of doing kind of responsive design where basically at the medium size, they'll take into three, but otherwise it'll just be a single column. So that way it can make everything a little bigger and so on. All right. So let's add those. Just really quickly. Product list. All right. Let me know if that's not up there or if anyone's having any issues. But you should be able to pull down and get caught up. Okay. So next up, um, what we're going to do. So this is pretty cool. We got, we got our, uh, a list of products, but we're only getting the first 10 and we want a way to load more products. So this is where, uh, what I, what I personally think this is when kind of the power of server components comes in. So in our index dot server, we hard coded this first value, but what we actually want to do is we're going to pass this in as an additional variable. So put in first here, it is also going to be an int and then we'll just pass that, replace our 10 with the use of this variable. And then we need to pass that into our query, just like the other ones. And that should basically all still work. Nothing should change. We've just, now we're, we're setting up here one further layer up. We're going to do first. Um, and actually we're going to grab this out of the props of our page level component, which in this case is index. And we're just going to give this a default value of 10. So first it's going to be 10, which is coming into our page props. And then we're just passing that right along to our query. So nothing's really changed here yet. If you want, you can get rid of this redundancy and you can just save. So first, first, and this is the amount of products that we're going to get down here, but what is this? What is this? So where this comes in is where part of hydrogen is that we actually have a concept called server state, which is a way for server and client components to communicate. If we change this to four, just so that we'll just see this a little better. We'll only get four components now. Yeah, that's better. So what we want to do is we want to have a load more button that is going to change this to be, to load more products. And so it'll continually increase this as we, as we load more. And this is sort of like a poor man's like, uh, uh, like continuous loading. If you want to do this a bit more complicated, maybe we can do it after it's like, you would use a cursor here and you would pass the cursor along. But for now, let's just do, let's just, all we really want to show is how to manage this server state from a client component. So let's create that client component. It's going to be in components and we're going to call it load more client.js. So we'll export default load, uh, sorry, function more. And what we're going to want to do is we're going to want to wrap our entire block of product listing within this component and append a load more button at the end. So let's create a div and let's grab our children. We'll grab some of the props up here. And then underneath that, we're going to grab a button and we'll say load more. Okay. And just install children correctly. Now we want to use this component. So we'll import it more from component slash load more. And we'll wrap our product list in the load more. Now if all goes well, we should see load more at the bottom and we do, but it's so small. So we're going to make it bigger by just adding some tail end styles. So class name, uh, say BG black, say text color. What is the, what is the way to color text? Text white. There we go. And we'll give it a little bit of padding, uh, on the y-axis and even more padding on the x-axis. So that's looking better, better. Um, let's, let's, let's actually, we'll say just, uh, actually note that's probably fine. We'll just give it a bit of, um, yeah, no, we'll wrap this in a div. EY, just something like pretty big, like 10 and text center. Close off this div over here. We have this like load more button currently doesn't do anything. So let's add an unclick and load to it. So what are we going to do here? What do we, what do we want to happen when we put, well, we want to set that first variable in our server state. And the way that we do that is we use another hook from hydrogen called use server state. So it's const set. So, so very similar to a lot of kind of state management hooks. We'll say const, uh, use server state equals, um, the result of, uh, sorry, set server state. Um, is the result of user state and inside of our on click handler, we're going to set server state and we're going to set the first, uh, value. And let's just for right now, set the time. Let's see here. It's like we've got an error because again, slash client. I think that should do it. So we've got four products. We'll do a load more here and nothing happened. First, first, first, let's see what happened here. Okay. What did I do wrong? Hold on a second. Let me just give everyone a chance to catch up as well. All right. Let's do a little debugging then. Okay. A button I would imagine is working. So let's just see what's going on here. Once a log, our server state here. Getting four. And let's load more. We're getting 10. Oh, it is working. No, it doesn't work. All right. Nevermind. I think it was just, uh, maybe I saved and it might refresh at a weird time so it didn't look like it was working. Uh, let's see, unique key prop. Where's that? Probably in here. If you want to get rid of that unique key prop area or, um, console log there, just give, uh, give a key value to our product card and make that product out ID. Actually make it go away. Great. So we click load more and we get 10. We should. Yeah, there we go. So, um, I think my internet is also just like a little bit slow. So when we, so that's great. So, but if we click it again, we're still just going to have 10 and it's not going to change it. So we actually want to pass in, uh, a little bit more information to our load more and just let it know what our last, or maybe we'll call it current, uh, is going to be what we last sent, um, what we last sent along. And then inside of load more, instead of just saying 10, we'll just say first plus four or something. And let's just grab first. And again, you might want to do this a cursor, not this particular way of paginating, uh, with graphql, but, um, if you don't know what I mean by that, then that's fine. You don't, then you'll, you'll learn it when you need to. So first plus four first, why didn't we in here? First is that, Oh, cause I called it current. My current and current plus four. All right. Let's see this work. So we should load, we should get the next four and then we get the next four. If we load again, hopefully we do. So that's cool. But why is this actually like, why is this so impressive? Well, what's neat is that when we do this, if you open up the console or your developer tools, like I have. And you go over to the network and you, you know, just look at all or look at, uh, XHR requests. You'll see that, uh, never are when we load more, are we actually hitting our graphql api? What we're actually doing is just sending along this slash react. Uh, or we're just getting this slash react endpoint back, which has this serialized data here. And this is kind of a represent, this is a representation of our client components and also another representation sort of of our like whole tree. Don't want to focus too much on this because it, you know, it may change, but just the real takeaway here is that there's a serialized JSON response that is streamed back to the client, which is going to be more performant than hitting the graphql api directly from these clients, from this like client component, this load more. So now that we've done that, it's like trivially easy to do more here. Let's just do a quick time check. I think we have a little bit of time here to just say, well, what if we wanted to, um, have this happen more in, not with a load more button, but just on scroll. So the way that we can do that is, uh, we've pre-installed a library, a library. Actually, before we do this, let's do another commit. Matt, we have some people in the discord that are just asking if we can take a short break. Oh, yeah, yeah. This is probably a good spot to, uh, to stop for now, just for five minutes. All right, cool. Yeah, we'll do a short break. I'll commit these up and then we'll reconvene. We'll do, um, how are we doing for time, do you think, Kathryn? Um, we're a little more than halfway through. I think we might want to skip the, the little bit with the infinite scroll that we were going to do and we can come back to that maybe at the end just to make sure we have time to get through the rest. Yeah, sure. That sounds good. We'll get a bit fancier with this load more later if we, if we have time, but, uh, otherwise this is kind of, hopefully, I mean, this shows the main concepts of what we're really trying to communicate, the load more or somewhat like a sprinkle of a little bit nicer interactivity, but yeah, sounds good to me. I'll just commit these up and then we'll take a short break. Great. Okay. Okay. Okay. Okay. Okay. Okay. Okay. Okay. Okay, I think I'm gonna get started then with the next part. If everyone's ready. Um, so Matt pushed everything up to the live branch. So go ahead and pull that down. If you want to work off of the same branch that we've been on, I'm just going to continue his work from there. And before I get started, I'm just going to do a quick recap of everything that we did because that was a lot and it was pretty cool. So Matt went through and built first a server layout component, which is a shell of our application where we did our first query to the storefront api to get the shop name. And for that, we used the U shop query. We then went on to build a product list page on the index page. And in that we did another query with U shop query, and we used a product provider fragment provided by hydrogen to build out the query to query for our products. We then built out a product list client component here. Um, the product list just iterating over the products to render a product card. And then the product card component itself was making use of a bunch of hydrogen components, including the product provider, the selected variant image, product title and selected variant price. We're also using the link there so that when we clicked on a product, we can go to a product page, which we haven't built yet, but we're going to be building now. And lastly, before we build in just to finish up the recap, uh, Matt built out a load more button that used server state and shows you how to communicate state between client and server components and demonstrated how you can use that to make additional requests and load more product data. So to take things from there, we're now going to be building out the product page. So if we go ahead, let's look at like little river, for example, this product here, if we click on it right now, it's just going to a page. This page could not be found. And you see the URL up here is products slash the product handle that we get back from the storefront api. What we're going to do is we're going to build our first new route and our first new page in hydrogen. And the way we do that is by going over here, actually, let me just increase the size of my, of everything here so people can see. Does that look okay to you on your end? Just want to make sure you can see it too. Yeah, it looks good to me. If you don't mind, just you, can you open up a, um, a file in it? Yeah. Yeah. That's better. I think that's big. If anyone disagrees, just say so in the chat. Yeah. Let us know. So let's add, let's add our first new page. We're going to do that by going to the pages folder here. And like I said earlier, any new components that are added to the pages folder are actually going to be registered as routes in our application. What we're going to do is we're going to add a new folder here and we're going to call it products. And this is going to correspond to this little part of the URL right here, products. Next thing we're going to do is we're going to build a page. Let me just right click here. There we go. We're going to make another page that we're going to call handle in square brackets like this dot server dot JSX. What that means is that anything after the slash after products. So whether it's a product handle like little river or any other string, like any string or just a jumble of letters, that's going to be our handle. And we're going to be able to pull that handle out using the use params hook provided by react router Dom. And then we're going to use that handle to query the storefront api for all of our products. So let's give that a shot. I'm just going to first build out a little dummy component here. We're just going to say export default function product. And let's just return here a little paragraph that says hello. And we're going to put, we're going to print the handle there. So let's go ahead. We're going to use the use params hook from react router Dom. And we're going to extract the handle from it. So we're just doing a little bit of object destructuring here. Let's go ahead and use params. And let's not forget to import it. Otherwise none of that's going to work. So let's import use params from react router Dom. And you might be wondering, oh, where's react router Dom coming from? Well, we've already installed it and it's part of the package dot JSON. So you don't have to worry about adding it or anything. It's already included in our project. So here we go. We're extracting our handle and let's go hello handle just to print it out. And one last thing, when you add a new page in your products here, we need to make sure it gets registered as a route. So we're actually going to restart our development server to make that happen. So I've just canceled it here and I'm using yarn. So I'm just going to rerun yarn dev. If you're using npm, you can do npm run dev. There we go. And if I go here and we wait for this to refresh, here we go. It says hello little river. We're in business. Let's do this. The next thing we're going to do is let's actually query for our product data. How are we going to do that? We're actually going to use the product provider fragment like we've already used previously with Matt. So just to save on needing to type it out, I'm going to go over and do a little bit of copy pasting. So let's just grab this query here from our index dot server file. And let's go ahead and paste it down here in our handle dot server dot JSX file. Right away, I saw a little flash here saying GQL is not defined. So don't forget to import that. So we'll import GQL from graph QL tag. And we're also going to there was another error saying product provider fragment isn't defined. So let's go ahead and import the product provider fragment from the shopify slash hydrogen bundle. I know previously when we were doing our demo, we were using shopify slash hydrogen slash client. And that's only for client components. The shopify slash hydrogen slash client bundle only exports client components because those are the only things we want in our client bundle on the browser. When you're on the server, however, you can just import directly from at shopify slash hydrogen because we don't really care for importing server components there because we're on the server anyway. So let's go ahead here and just adjust our query. We're going to rename it. Best practice is always good to have a name for your query. So let's just call it the product details page. We're going to keep all of the same variables because we're still making use of the product provider fragment, which needs these variables. But let's get rid of this first one here because we're only retrieving one product each time. And we're actually retrieving it by handle, right? So what we're going to do instead of using the products field in our query, we're going to query instead the product field. And all we have to do is the storefront api here is expecting for us to pass a handle. The handle that corresponds to the product and it's what we retrieve back from the storefront api. So let me just go up here and add handles so that we don't forget to define our variable for graphql. We're going to say it's a string and it's mandatory. We always have to have it. And lastly, we're just going to clean up our query here because we're only querying for one product. And this isn't actually a relay object because we're just getting a single product back. So we can go ahead and remove the edges and the nodes section there and just use the product query. Perfect. So let's go ahead now and actually use that query. Like we did previously, we're going to use the uShop query hook from hydrogen, which lets us query the storefront api from a server component. We'll go ahead and extract the data from that response with object destructuring. And we'll use uShop query and we'll pass our query in like this. And we'll also pass our variables in too. And to avoid needing to copy everything down, I'm just going to go back to the index.server and I'm just going to copy these variables here from our previous one. I'm going to paste that there. We'll go ahead and we'll take the same number of everything else that we had previously. And don't forget, we also added a handle variable to the query. So let's pass that through as well. And we'll just say we're using this handle. Now let's console log our data just to make sure that we have it and that it shows up as expected. So let's see what happens. And again, we're console logging in a server component. So if you go and you look at your console in your browser, you're not going to see anything there because this console log is executing on the server. Instead, you're actually going to see it down here in your terminal where you can see your server commands. So there we go. We can see that our product that has the handle little river, we're actually getting some information back. Success. So what are we going to do next? Well, obviously we need to display a little bit more than just hello little river on our product page. So we're going to build now something that has a bit more interactivity to it that allows people to select different variants associated with the product and to add items to their cart and even check out with a product. And since that has interactivity in it and it's no longer querying the storefront api, we're going to build a client component. So let's go ahead to our components and build a new file. I'm going to call this the product details dot client dot JSX file. Let's just go over here and import it before we define too much of it. So we'll just say import product details from, and we need to go all the way to our components folder and grab the product details dot clients. What I'm going to do is instead of returning hello handle, I'm going to return product details and let's pass the product information through as the product problem. So we'll just do this. Oh, let's go over to the product details now and actually build a component that we can use. So we'll export default function product details. We know that we are receiving a product prop and let's return for the moment just to show that this is working. We'll return hello product title. So it's the same thing we were returning previously. So there we go. If you look over here after we refresh, hello little river. Let me just say hello from product details. Boom, there we go. Now there's one thing I want to highlight here is that we're using the URL. Anything that comes after the slash products is going to be used as the handle, but it's entirely possible that someone might put in like mistakenly write the handle. Maybe they'll write little river and add an S to it and it's plural. Well, we don't have a handle. We don't have a product associated with the little rivers handle. So what should we do here? Normally you would show a 404 page, right? And so that's exactly what we're going to do. We actually have a component that we pre-built before this workshop. You might have noticed it. It's called not found dot server dot JSX. And this is essentially our 404 page. It's very lightweight just for the purposes of this demo, but you could build it out more to look like what you would want your 404 page to look like. Right now we're just returning this page could not be found in a paragraph. And so what we'll do is we'll go over here to handle dot server. And in the cases where our data is undefined, we're just going to return not found. So let's choose if data is equal to null or if data dot product is equal to null. Let's go ahead and return not found. And don't forget to import not found also. Components folder. So we'll go ahead here. And it's a server component since it's just it doesn't have any interactivity to it. It's just opening a paragraph. And so now if we refresh on little rivers, we can see this page could not be found. So we're showing our 404. Awesome. I'm also going to, while we're on the topic of 404, I'm going to draw your attention to in the app dot server dot JSX file, we'll go ahead and open it. And you'll see in this default routes component that we're using that we have a prop called fallback. And to that prop, we're passing the not found component. This is just our generic for any route that's not defined at all. This means it'll always fall back to not found. The reason we had to return it from our handle dot server component is because, well, technically there was a route for it and it was trying to return something, but it didn't work. So in this case, we have to say, okay, specifically when the data is not there, where this product's not found, it's a 404 situation. While we're at it, I'm just going to go ahead and commit this. So let's just go get add, get commit page with 404. I'll push this up and you guys can go ahead and pull it down if you want to keep following from there. Let's get more into the product details. Let's actually build something out that's really cool. That looks like a real e-commerce product details page. We're going to make use of the product provider component that Matt already showed you. And we're actually going to make use of it with the product alias that hydrogen has. So the product provider can be accessed. You could just use the product provider that's exported from shopify hydrogen, or you can use its alias slash product that also provides handy namespacing around a lot of related product components. So what we're going to do here is, whoops, I'm going to screw up my html. That's what I'm going to do. Here we go. What we're going to do here is we're going to return instead a product. And remember, like I said, this is just an alias for the product provider. This is something you've seen before already. We're going to pass the product through just like we were doing with the product card. And if you remember, when we were dealing with the product card, Matt mentioned that the product provider expects an initial variance ID as well. So we're going to go ahead and extract that. So let's do const initial variance. And we could use the flatten connection, like Matt had said, but in this case, we know we just want the first variance. So let's just go ahead and go product.variance. We're going to go to the first edge and we're going to take the node from there. Let's go ahead here. Then we need to pass through to the product an initial variant ID prop and we'll pass it the initial variance ID value. Excellent. Let's display the title. That's the most basic thing to display, right? So let's do product.title. If you look here, oh, we're still on the 404 page because I didn't update, whoops, didn't update the handle here. So let's just go back to Little River. Here we go. We're displaying our product title. Another thing we would normally show on a product details page is the description. So let's add that. Fantastic. In this case, our product description is literally just some of the lyrics from Hey Jude. You'll notice that some of it is italicized and some of it's bolded and underlined. And that's because in the shopify admin, this product description was added using a rich text editor, which provides html. And what's happening is the product description is using the raw html component that hydrogen provides under the hood. And it's just outputting that and setting the inner html so that html ends up outputting on the page. Now let's display an image. We're going to use product again, and we're going to use the selected variant image. And previously, you probably remember we were using selected variant image as a component name, but right now it's namespaced under product selected variants. That's a nice handy little shortcut for you so you don't have to continuously add new imports here from hydrogen. And there's our image. Fantastic. Next thing we'll do is we're going to display the price. So a product itself has a price. It usually has a price range associated with it. And then you have to actually use a selected variant price in order to display an actual unique price. So what I'm going to do here is do product.selectedvariant.price. That's going to display the price for this variant. Now if memory serves me correct, I added a compare app price for this product. So this product is actually on sale right now. So there is associated with it from the storefront api, we will have been given a compare app price that shows the original price. You can access that by using the selected variant.price component again. But in this case, you want to specify the price type, which in this case, we're going to say is the compare app price. And there you go. If you take a look over here, this here, the first one you see is the default price, just the price that it's all for that right now. And then this price here is our compare app price. Super handy. Now, obviously, another thing you like to do on a product details page is you need to be able to add the item to the cart, right? There's not really much point if you land on here and you can't do anything with the product. So we have another little handy utility for that. So if you go to product.selectedvariant, there's a component called the add to cart button component. Now, this is a button that when it's clicked, it's automatically going to interact with the cart and it's going to add the item to the cart. And you might remember from the beginning when we were introducing hydrogen that I said that headless components never include hard coded content. And what that means here in the case of the add to cart button, well, we need to say add to cart or add to bag or buy now or whatever string you want to show on the button. We need to provide it since hydrogen is not going to do that for you. So here we're just going to specify add to cart. And we'll refresh. Here we go. You can see the button. Fantastic. Right now, if you click it, nothing's going to happen. Under the hood, something is happening. Let's go and inspect the console and just take a look at the network tab. You'll notice whenever we click the add to cart, we're actually doing a graph QL request and we're actually hitting the storefront api to add this item to the cart. Nothing visually is happening on the page because we haven't actually added any cart UI yet, which will be the next part that we do after this product page is built. Now, in some cases on some e-commerce websites, you might see a buy now button. So you don't need to add an item to the cart. You can just click buy now and you go right to check out with that. We also have one of those built into hydrogen. So you just need to use the product dot selected variant slash buy now button. And again, like the add to cart, you need to specify the content that you want to display on the button. So we're just going to write by it now. Let's save that. There we go. There you go. You can see the buy it now button. I know nothing here on the page is styled yet. We're just using the default styles that are coming from Tailwind, but we're going to get to styling shortly. So just bear with me. Now, if you click on the buy it now button, you can notice we're redirecting right to check out. So now if you want to start selling, like that's literally all you need, but there's still more obviously that we need to do. We still need to style it so that people want to buy things and finish up our product page. So let's go back. But at least that's exciting knowing that, hey, we can buy stuff. What else do we need on this page? Well, let's add a media gallery because like, yeah, there's one image here, but I know for a fact that I've added more images in my admin for this one product. So what I'm going to do is actually, I'm going to build a gallery component here. I'm going to just scroll down on this page and I'll just build out my gallery function, gallery. Now, something that's important to note is that, oh, let me just return null in the meantime so we don't have to see that error message. Something to note here is that the product component, product provider component, sets up a product context for any child component that belongs to it. And any child component can make use of a use product hook. And that's what we're going to do product hook. And that's what we're going to do right now. So we're going to do const media is equal to use product. Before I forget, I'm just going to scroll up here and import use product from shopify hydrogen client bundle. And now what am I going to get from the use product hook? It's going to return to me the product object, but it includes a lot more stuff than just that. The product context is actually, it's passing through all of the information that you've already retrieved from the shopify storefront api, but it's also doing a few different things. Under the hood, it is taking any connection fields that you have in your products. This would be, for example, a media connection, a variance connection, a meta fields connection, and it's automatically flattening them for you. So here, when I say, when I'm extracting the media from the use product, I'm just getting a flat array of media files from the storefront api. What we're going to do here is let's just build out an unordered list that has all of our product media displayed. So we'll just do media.map, and we'll go over all of the media. And we're going to use one of the hydrogen primitive components, media file. So we'll do this return. Let's do a list item. We're going to return media file. We're going to pass a prop media, and the prop that we're passing through is just the product media that we're mapping over. And I'm going to scroll up here, and I'm going to add the media file to our import. Boom, there we go. So as you can see, we had our original image up here, the selected variant image, but now we're also including a bunch of other media for this product. Now, I'm going to just mention here that in your shopify admin, you can add images, you can add videos, you can add 3d models to your products. You don't have to just add images. And you might be wondering, well, why are we referring to this as media instead of images, and media file instead of using the image component, for example. The reason here is that the media connection field from the storefront api will return to you all of your media you've associated with the product, regardless of what type it is, whether it's a 3d model, a video, or an image. And what this media file component does is it just takes the media object you've given it, and it automatically checks to find what type of media it is, whether it's an image, a video, or a 3d model, and it outputs either an image, a video, or a 3d model for you. So it's just a handy abstraction here for you so that you don't have to go and inspect the product media and be like, oh, do I render an image or a video? Let the media file take care of that. You have more fun things to do. Now, another thing you'll notice like, okay, these images that are being returned are quite large. I don't want to bombard my user with these huge images on the product details page. So what I'm going to do is I'm going to specify some options. Now, the storefront api, when you upload images to the shopify admin, they end up being hosted by the shopify CDN. And what's great about that is that you can adjust the parameters and arguments in the URL to make sure that you retrieve an image of the right size with the right format, with the right cropping, etc. So what we're going to do here for the options is we're actually going to just specify, give me an image with a width 100, height 100, and I want you to crop it to sort of maintain a nice aspect ratio for me, but just crop it in the center. So just take your big image, give me 100, 100 right in the center of the image. And that's what's happening. So you'll see here all of our images are small now. So we don't have to worry about the buyer downloading huge images that are just going to be shown really small or using up all their mobile bandwidth. So that's awesome. I'm going to go ahead and I'm just going to commit this for you, those who are following along. If you're lost, let's add added media and we'll push it there. Cool. What are we doing on time? Cool. 2.13. We're on track. What's the next thing we should do? We've got our media gallery. We're displaying our product title. We're showing the regular price. We're showing the compare at price. We have our add to cart buttons and we have our product description. It's not styled yet, but we're going to get there. But one thing that's missing is I know that I have two different types of variants for this product. I know I've got like a burgundy option for color and I think it was a teal option for color, but we don't have anywhere here to change that. We don't have anywhere anyway on this page right now to adjust that variant. What we're going to do is we're going to build out a product options picker. So let's just scroll up here and let's say under the price, but before the add to cart button, let's render an options component. Let's go ahead and build that. It's not defined because we haven't built it yet. So let's just minimize this gallery. We'll do function options and let's return null. Now, when we were building out the media gallery, I was telling you all about the use product hook and how it does a bit of processing on the product object for you. Some of the processing that it does is it actually sets up a bunch of state for managing the selected variant for you and provides you with handy callbacks for changing the selected variants and keeping track of everything related to selected variants. So we're going to use that here. Let's go ahead and grab the options, the selected options, and the set selected option callback from the use product that's returned. The options here is going to be an array that tells us what all the product variances are grouped together. So for example, if I know I have a product variance that are based on colors, I can get teal or burgundy. So I'm going to have like one item in this array with a title of color and with values of teal and burgundy. Selected options is going to keep track of what option is currently selected and set selected option will let us update that value. So let's go ahead and build this out. Take the media gallery. Let's do an unordered list and let's iterate over the options. We'll map through them. And for each option, let's return a list item. The list item will include a span that has the option name. Let's see how that looks now. So you can see right here, that's where we're starting to render. So the option name is color. And now for each value associated with that, let's render a radio input field. So let's do option dot values. We're going to map over each value. And we're going to return, let's open a little react fragment here because we're going to return an input, an input of type radio. The value is going to be associated with the value. We're going to give it an ID associated with the value so that we can associate the label with this input. And of course we need to give it a name. So we're going to give it the option dot name so that everything is part of the same field set. You know what, it's a bit hard to read this because all of these are going off the screen. So let me just do this for us. Makes it a little bit easier. Next, we're going to do a label to associate with that input. So let's make sure we use an HTML4 and we pass the value in since we want to refer to the ID that's here. And let's just output the value like this. Let's go have a look. Like this. Let's go have a look. Excellent. Here we go. We see our options, the name, and we see the different values associated with it. And now if you remember correctly, this image up here, I'm using the product selected variant image. But when I go and I change the selected variant here, nothing's happening. And that's because we haven't actually updated the selected variant using the state callback. So let's do that here. We'll go into the input. And whenever the input changes, we'll add just an add and on change. Let's take the event and we're going to say set selected option. We'll pass the option name, which is color, and let's pass the value. In fact, here this should be event target value because we want to use the value associated with this input. And lastly, we're going to want to make sure that we use a controlled form component here. So we're just going to just check if this, if selected options with this option name is the same as this value, then this item should be checked. So let's do a little refresh here. All right, let's see. Moment of truth. When I select this, yes, now the image updates. So it's nice little interactivity here. And part of this interactivity is again, why would this could be a server component? Because server components can't handle this state and context that we need for this interaction. Now I think we can probably start to style this. I think we've got the full functionality here. We have the selected variant image. We have a list of media here. We've got the title. We've got the price. Let's add some styles to make this look a little bit nicer. So you can copy the styles that I'm giving to the doing here, or you can just do your own. I mean, the great thing about hydrogen is that you have the freedom to do any styling you want. So actually, let me do this instead. I'm going to wrap everything in a div and create a grid. And you know what? Before I do that, let me just commit what we have. And I'll push it up and you guys can pull it down if you're lost or need to be or have stuck on anything. And I'll do a short break to drink some water, so just bear with me for a second. Okay, let's keep going. Let's add a class name here. We'll set up a grid and we're going to do three columns for this. That's three. Is it looking? All right, looking terrible, but we're getting there. Piece by piece. Let's put the selected image and the gallery in the left side of the page, and let's make it fill two columns. How does that look getting there? And let's put all this other information in a div as well in the third column. Perfect. So it's looking good. Let's finish up the gallery over here. Let's also put the gallery in a grid as well. So let's do class name, grid, grid, calls. Let's put four. How does that look? Now this is one of the really nice things about Tailwind is that the styling really doesn't take long at all when you're really familiar with what all of the utility classes are. Let's put a gap between every image of two, and that's just going to be for the grid gap spacing. And maybe let's make the media files look a little bit nicer. Let's make a class name, and let's put like rounded edges on them. That looks nice. And maybe let's add like a little margin between these and the selected image. So let's do a margin top here, and let's just add a margin top of two. Let's see how that looks. Perfect. Let's go up to the selected variant image now, and why don't we also add a class name here to get large rounded edges. Fresh. Boom. Looks great. Let's add a little bit of spacing between these columns here in this third column. We can do that by adding a margin right with MR, and let's add four here. See how that looks. Great. Let's emphasize the title a little bit. It's so small there, so let's make it text 3xl, so super large compared to everything else. And why don't we make it semi-bold too to stick out a bit. There's not a lot of spacing between everything here, so I'm going to use one of the Tailwind utility classes for spacing everything on the y-axis. So I'm going to use space y2, which puts spacing between each element that we see here. I'm going to go right to the add to cart button, and I want to make that stand out. So let's do class name bg black. We'll use text white, and let's do padding. The x padding will be six, and the y padding is going to be two. That looks a lot better getting there, and let's do rounded. Cool. And why don't we do something similar to the buy it now button. Make that stand out so that people are feel like clicking the button, and instead of making it bg black, we'll add a border. We'll say border, border black. We'll keep our text black, so no need to change it there. Fresh. Excellent. You got their minor thing. You got two px's instead of a px and a py on those buttons. Nice catch. That would explain why it didn't quite look like what I thought. Thanks everyone. Okay, so I'm going to speed through a little bit. I could keep styling here, but there's one thing I want to show you since we're almost at 2 30, almost two and a half hours so far in the workshop. We talked about render props and render prop functions as a way of customizing things. So you've seen right now, you've always seen us using pass through props and passing class names through, but I'm going to show you how to use render props for something like the product price. So what we do here is the product price accepts a function as a child, and it's going to pass price information through as the first argument to that function. What I'm going to do is I'm just going to console log the price here so you can see what's in this object. And since we're on a client component, it will be logged in the console here. So let's scroll up. Yeah, I always forget to add the keys. I'll fix that later. If you look here, the object that's passed to the render prop function includes an amount, a currency code, a currency name, as well as all these parts here that are just sort of the price that has been broken down. And a lot of these are returned from the international number format javascript object and all the utilities that are associated with it. And what I'm going to do here is I'm just going to make this look a little bit fancier. Let's instead of just showing the price by default, let's show the price dot currency code so we can say that it's in Canadian currency. And actually, I'm going to put this in a fragment. And let's do display the price currency narrow symbol. So this is the little dollar sign that we see. And then here I'm going to output the amounts. And what I'm going to do is I'm just going to map over the price parts. I'm going to just return the part value and I'm going to join them together like this. Let's see how that looks. Price parts is not a function. Price parts dot map. There we go. So you can see it there. Now it looks slightly different than before, but let's add a little bit of customization to it. So let's say class name. We want this to be small text. We want it to be font, like text color gray maybe. Text gray 400, let's say. There we go. We're starting to add a little more customization there. I'll add a little bit of a margin on the right. We'll do that. So here's an example basically of render props and using render props to take the default output and just enhance it a lot more by providing your own function that returns its own JSX. I'm going to go ahead and commit this that you guys can follow along again. And I'm going to take a short break very soon. I just want to show one thing that'll just be sort of a bit of a copy paste off of some notes I have since we're a little bit short on time. But it's possible in hydrogen to build very custom static pages for specific products. And that's done by, for example, instead of using the handle dot server dot JSX to render a page, you can specify something a lot, you can specify something much more specific to use for rendering. So in this case, I'm actually going to use, let's see, I have some notes here. I have a product in my store that's got a handle called Sparkling Shape and I want to market that and make this really cool landing page for it that is going to be unique from all the other pages that we have for products. And the way I do that is now by defining this Sparkling Shape dot server dot JSX file when we try to hit that by changing this here. Actually, whoops, just let me restart the server because I added a new item, a new page, excuse me. And now that I have this new page, it's going to use the Sparkling Shape dot server instead of the handle since it has a more specific route it can use. And let's just do a really quick example here. Sparkling Shape, return, let's just return Hello World here, make sure it returns Hello World. Sparkling Shape, there we go. And quickly, and I'm sorry about this, I'm going to do a bit of copy pasting just to speed things up. What we're going to do is we're going to use, we're just going to show an example of querying for that data. So we're going to use the same query we had previously in the handle dot server page. Let me just copy paste that and put it down here. We'll use that and we know that the handle we're hard coding to be Sparkling Shape, which corresponds to this handle. And we're going to return in this case, let's do something that is a purely server component. It doesn't have any clients, any client features in it. So let's return here just a paragraph with your product, data dot product title. And maybe we want to also return a media image so we can use a media file component here. Import it from hydrogen at shopify slash hydrogen. And let's just pass the first media file into this. So we're going to use Flatten Connection just to make this a little bit simpler, data dot product dot title. And let's just take the first media that we get from there. And let's see, don't forget to import Flatten Connection. And don't forget to import graphql. Let's go here. And use shop query. And product provider fragment. Oh my goodness, this is why we don't like to copy and paste. What do we got here? Looks like we have a little mistake. Cannot read property undefined. Oops. We should be passing the media connection here. There we go. We're displaying a new entirely custom page. And on this page, since we'll say we want to keep it just operating on the server with no client, we don't want to use any product provider functionality since that uses a context, then we can make use of a lot of primitive components like the media file or the raw html, for example, where we can pass in a string of just the data dot product dot description. Which you need to import. And actually, I need to say description html here. I used the wrong field. So there we go. And these components, media file, raw html, these can execute on the server, which means that this entire page, none of the, there's no client code that's being passed to the client. There's no client bundle here. It's just all rendering on the server and just being passed down the wire. I'm going to go ahead and save this and push it up. We're going to take a short break for like, let's just do like a short three minute break, and I'm going to show you how to build out a cart component. And then we should be finishing up the workshop. So if you have any questions, please take them into Discord. Hey, Catherine, I think you need to push those. Yeah. Good point. There we go. It's coming. Okay. Okay. Okay. Okay. All right. I'm going to get back to it. Now we're going to start building out the cart. Okay. Just a moment while I get things ready on my end. So we've built out the product list on the index page. We built out a product display page with all the product details. We use the product provider again. We use some of the use product hook. We built out our own product option selector and we also use the add to cart and the buy it now button. The buy it now button we know works already. If you click on it you're going to get redirected right away to the shopify checkout and if you open the order summary you'll see that you have the item that you've picked already and you can go ahead and purchase it. But I mean usually people they want to add more items to their cart and then check their cart out. So let's go ahead and build the cart so that we can do all of that. What we're going to start with is let's go and make a new client component and we're going to call this cart.client.jsx and it's important to note that the cart has a lot of interactivity to it. It's actually I think the only hydrogen component that makes queries on the client side so we need to make sure that this is a client component. It will only execute properly on the client. So let's go ahead and define this. So we'll export default function cart and for now I'm just going to say hello cart. Let's go down here to our layout component and what we're going to do is we're going to render the cart just right here. Let's make sure we import it. Now you might be wondering hey we're using a client component on the server. That's fine. The layout component will just it's not going to render the cart on the server. That's only going to happen when the all of the information is sent to the client. So let's go ahead and look here if we refresh. Ah you know what it's not displaying because we actually need to use the layout in the product details page. So let's just go back to handle that server here and let's wrap this in the layout component. That's and we'll import layout from components here. There we go. This looks a lot better. You can see we've got our cart rendering just at the bottom here. I'm going to go back to the cart page and we're going to start using the cart related components in the cart context. Now if you remember if we go back to the app.server file that we showed away at the beginning of the workshop you'll see here that we're using a cart server provider and a cart provider component here. And this sets up a context global to your entire application so that all the children in your application have access to the cart context. What that means for us is that in the cart we can make use of really handy components such as the cart lines component and the cart line component. Let's go ahead and import those. Let's go ahead and import those. Again we'll do it from the client bundle here. And what we're going to do is let's use the cart lines first. What does this component do? The cart lines component accesses all of the cart merchandise lines in your cart context and any children you define within this it will apply to every single cart line for the cart. That means you can do something like this cart lines dot cart line title. You can even do cart line dot image and if we render that something isn't right here. Check the render method at cart. Can I just try this for a second? Aha that's one of the things. Okay this should be cart line dot product title. Here we go. So now if we look and we scroll down we can see we have one cart line rendering because we've already added this item previously to the cart. So we have little river with its image. Let's make the image a little bit smaller. We'll use the options again like we did previously. Let's just do like a width of 75 and a height of 75 and we'll crop it in the center. How that looks? Excellent. Let's also display the cart line price because it's a good idea to know what it is you're buying. Perfect. Also it's pretty standard to show the quantity that you've got for your cart line. So let's do cart line dot quantity. Fresh this and you can see a little one here. If we add to cart again we see it go up to two. If we click add to cart again we see it go up to three. Fantastic. And sometimes in the cart you also see quantity adjusters so that you can increase and decrease directly in the cart and we actually have one of those. So you can use cart line dot quantity adjust button and let's just add a little plus here to show that this is to increase. There we go. We'll add a also another button for decreasing the quantity. So we'll do quantity adjust button and we'll do minus as our symbol and what we're going to want to do here is by default the quantity adjust button will render for only increasing in the item or increasing the quantity and so here we're just going to say adjust decrease and this will signify I want to decrease the quantity. So now if I go ahead and click these there we go we're increasing the quantity and we're decreasing the quantity and it's updating right away in the price that's rendered as well. Lastly in the cart there's often also a remove button because I actually don't want to buy that item anyway so let's just add a cart quantity adjust button. We're going to say adjust this time is remove and let's just add a little bit of remove text here. And then when that's clicked we'll remove an item. We can add a few more things. Let's see what time it is. It's 12 45. I'm going to zoom through this really quick so that we can cover everything that we want in the workshop. So I'm sorry that we're not going to get to a really nicely styled cart but it's showing you the main functionality that you can get right out of the box really quickly. So when a cart often when we display the cart we want to show the subtotal and the total as well as like checkout buttons because we want people to buy the cart. So let's go ahead and use a component called cart estimated cost and what that's going to do after I import it is it's going to automatically display whatever amount I specify here and here I'm going to say I want you to display the subtotal of the cart and that's what you're seeing right there $12. Maybe I also want to display the total price as well so let's do oops cart estimated cost we'll do amount type total and it will display the total which just so happens to be exactly the same as the subtotal at the moment. If I want to add a label to it let me just put subtotal in fronts and we'll put total here as well. Lastly let's add some checkout buttons so that we can actually go to check out with this cart and we've got a cart checkout button component here and it's like the add to cart button we're going to have to specify the content so let's say check out now for example and let's import that and there we go we can see our button and if I click it I'm automatically going to go to checkout again with my item. So let me just take a look at time Matt what do you think do you want to pause now so we can go on to custom responses or do you want me to go on a bit more with the cart? It's up to you I think a few minutes for custom responses at the end would be good up to you I think. Probably a good idea I guess to leave some time at the end for questions as well there's still a bit more for cart I would like to do because ideally you know you don't see the cart being displayed at the bottom of the page here usually you see it up in the top corner and it pops in and out if you had a bit more time I would get into how to build you know the state to manage that and pop the cart open and closed but just so that we don't miss out on the cart on the custom responses stuff I'm going to end it here. I definitely recommend that everyone check out actually I'll send a branch in the discord but there is actually a branch called slash cart in the hydrogen workshop that goes into a lot more detail about stuff I just don't have time to do right now so you can take a look there to see where we wanted to end up eventually and if you have any questions you can always email me or ask me in discord so let's just add this stuff I just took over your screen but if you're just committing it yeah just I'm just committing nothing fancy go right ahead all right so let's get back over to where we left off here just pulled down actually do one more poll here just to be right up to date with Katherine's last work great so the final thing that we are hoping to show was just this idea of custom responses with custom responses you can return other things that are not necessarily react pages from the from the pages directory so what we're going to do is we're going to go into our pages directory and we're going to add a new a new file here and we're going to call this so you like the there's lots that you can get with custom responses but we're just going to quickly show this idea of maybe you wanted to generate a pdf catalog dynamically from your store data so without a file here called catalog.server.tsx and what we're going to do just because we don't have the time to totally run through this we're just going to paste in some some already done code so I'm just going to grab this and we'll just step through it fine so starting from the top here we're using a lot of the same utilities that we've already seen throughout this workshop but we're also adding pulling in a bunch of stuff from this react pdf render and this is the library that is third party library it has something to shopify but we're going to use it to or hydrogen but we're going to use it to render out the pdf there's some specific styles that are there for this pdf that we're just going to glance over but if we just scroll right down to the function that's actually going to produce the pdf you can see that we also in addition to server state and other stuff that we get in these page level components we also get a response object and we're going to first call the very first thing we're going to do is call response do not stream and this tells react that so this tells hydrogen that we want a different response body than the react rendered html so this allows us to kind of if we look down at the end here we can send down we can send down a different response to the client so in this case we're sending down the like we're rendering our component rendering it to string this is from the pdf library setting the response headers to application pdf and in inside of the body of our component we're you know just getting the data from our query botany edges and building out a pdf using the api i don't really want to touch too much on this because you know it's specific to react pdf but basically creating a page for every component and then creating a bit of a title page and then rendering that all as a document setting the header and then rendering that out to the client so if this all works correctly i'm just going to restart the server because that is a new one a new page level component at this point we should be able to go to slash catalog in our shop and hopefully we'll get a pdf catalog of all of our products well the first 10 of them into a pdf with their descriptions and title and the vendor and we could add more to this i mean this is just one example of using returning it's kind of like almost building out it something other than just like the react page so you could use a csv export here you could return json you could do i don't know this is where you can get creative so this is the the idea of custom responses that we just wanted to touch on briefly as it is like something that would be pretty powerful within your shop and one of the you know benefits of using something like hydrogen and and having the full access to to the the lifecycle of a of a page getting built about nine minutes left we were going to touch on deployment as well but we don't have time so what i will say is that if you want to deploy your shop when you're done developing um there's a docker file included in the repo and you can use anything where you could just throw up a docker instance so like for example fly.io was the one that we were going to use um the instructions to do that are simply follow the fly.io instructions and type fly fly controller launch just launch it and it will show up or it should show up um so nothing too complicated there look out for uh deployment um you know more in the way of deployments coming out of shopify using a service called oxygen but um that again is is coming in the future so for now you can just use the docker file and i think that's probably i don't know kathryn if you want to do touch on anything else within the next uh eight minutes but um yeah no nothing on on my end i'd probably just emphasize everybody if you're interested please go to shopify devs dot uh shopify.dev slash hydrogen and sign up for the mailing list um we'll also post a link in the discord channel to just get your feedback about the framework so far we'd love to know what your early thoughts are on what you've seen today is and maybe what you might want to see what you might want to see improved etc and hydrogen so please keep your eyes peeled for that i think other than that i guess yes thank you so much everyone really thrilled to get to show hydrogen to you today
171 min
21 Oct, 2021

Watch more workshops on topic

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career