Transcripts
1. Introduction: Hello, students and welcome to this class on Shopify Hydrogen. Shopify's React-based
framework for building custom storefronts.
I'm Christopher Dodd. I'm a freelance
web developer and top teacher here
on skillshare.com. Having specialized in
front-end web development on the Shopify platform for the
last three to four years, I've spent a lot of time
customizing storefronts with one of these,
a Shopify theme. But in mid-2022, Shopify launched a
new sales channel on the Shopify platform, allowing merchants to host a React project on
their Shopify store. This new Hydrogen sales channel paired with the Hydrogen
framework of React Hooks, components and utilities
built specifically for Shopify storefronts
offers a brand new way of customizing the online
store experience for customers and a new way of
developing for developers. In today's class,
we're going to go through the Shopify
Hydrogen framework, learn about the key
surrounding concepts, build our own simple
storefront using the system. I'll show you how
you can install your new app to
the Shopify store. For those of you who
are ready to learn this next generation paradigm for building
storefronts on Shopify, click on the next video and I'll see you on the other side.
2. Key Concepts: Before we get started building our own Hydrogen storefront, it's important that we gain an understanding of a
bunch of key concepts that all come into
play when building a custom storefront
using Hydrogen. First of all, when
I say Hydrogen, I could be referring
to two things. Most of the time I'll
be talking about the Hydrogen React framework. But when we actually
start to deploy our Hydrogen app to our store, the name of the sales channel on Shopify is also called Hydrogen. Technically the name of the
hosting solution that Shopify provides for Hydrogen
storefronts is called Oxygen, but it shows up on
the Shopify admin as Hydrogen for whatever reason. So keep that in mind. As for the Shopify
Hydrogen framework itself, it's designed to sit on top of an existing technology
called React. Some of you might be familiar and/or skilled in React already, but regardless, we're
going to briefly go over React in the
following video. It's definitely not going to be a deep dive into
React in this course, but it's important to
understand the basics of React in order to use Hydrogen. The other aspect which is also
important to understand is how we're going to retrieve data from the Shopify back-end. This is done through what's
called the Storefront API, an API that Shopify provides
for retrieval of data relating to customer-facing
experiences. The API is what's
called a GraphQL API, which means that
instead of hitting different endpoints to get
different bits of data, there is a single API endpoint that our project will connect to and will request
the specific data that we require via
what's called a query, which looks a little
bit like this. These concepts alone could
be a whole other course. So in the next two videos we'll take a look at
each individually and then I'll talk a little bit about the Hydrogen set
of hooks, components, and utilities that
play into this setup.
3. React: In this very important lesson, we're going to go over some
of the basics of react. If you are already
experienced with reacts, you can feel free
to skip this video. But for the rest of you, let me just say that you
shouldn't need to do a full course on react in
order to work with hydrogen. Before I picked up
hydrogen development, I had studied a little bit
of react over a few years, but it's not something I'm particularly expert in at
this point in time and yet I was still able to pick up hydrogen development
pretty easily. No, you don't need
to be inexperienced react developer to build
hydrogen storefronts. But if you have little idea
when it comes to react, definitely listen up to
what I'm about to tell you. React. What is it? React is a JavaScript library for building user interfaces, but it's often referred to
as a framework because it is a legitimate alternative to JavaScript frameworks
like Angular or view, two very popular frameworks you may or may not
have heard of before. You may be wondering
what the difference is between a library
and a framework. Basically, I'd say
that frameworks are way more opinionated. They force you to build
apps in certain ways. Whereas a library, you can just import whatever modules you like and just use them
however you need. Let's take react, for example. Here's an example of how
simple react can be. I've got a reacts playground open here at playcode.io/react. This is just an online IDE for testing out
some reacts code. All I did was go to
this address and now I've got a basic React app. I've got my
index.html file here, which essentially
just brings in the index.jsx file through
the script tags. Then that plugs into
this root div here. If we look at our index.jsx, you can see here we're
using create route on react DOM to plug into that route
element and render our app, which is right here. A few things I want to draw
your attention to here. You can see here what
packages we're using. You can see here we're
using react and react DOM. That's important to note
here because react is a library that can be used on a range of
different applications. It doesn't necessarily
have to be a front-end web
development thing. It doesn't have to be
a web app or website. It could also be a
native phone app. In the case of React Native. That's why react is it
stand-alone package, but then you can use
something called react DOM. That is what you use to
create web apps and websites. It applies all the core
functionality of react with the environment of our
front-end web development. The first thing you'll
note is at the start of virtually all these JSX
files is import statements. As I said, react is
very modular and so all we need to do in order to use it is to make sure
we're importing it. In both of these files,
you'll see that you import react from react. That might sound obvious here, but where this is looking up is inside your node
modules, see here. You can't really see it in
this file browser here. But when you install react
on your computer and create a package.json file, you'll see that it will also create a node modules folder, which will install all
your node modules. We'll see that a
little bit later if you're not familiar with creating JavaScript or
node-based projects. First things first we're
going to import react. Then a common pattern
you'll see is we're going to create a functional
component right here. You'll see it starts off
as a function and then we return this
HTML-looking stuff. Now, what is this HTML stuff? This is actually called JSX. It looks like HTML, but it's not actually HTML. What is happening
is this is getting compiled into HTML and
rendering on the screen here. You can see here we've got an H1 Hello React and start editing to
see some magic happen. This looks exactly like HTML, and it's producing
HTML over here. But this is not HTML. Let's take a quick pause
on JSX because it is important and cover
some things with JSX. What I'll do and there's
enough for the app. But just to demonstrate JSX, I'm just going to create
another JSX file here. We can call it script.jsx. I want to show you a few things. Specifically, I want to
show you some stuff where JSX differs from
the syntax of HTML. It looks very similar as I said, but it's not the same thing. Here's some differences. One of the differences is that there's no
hyphens in JavaScript. If we say for instance, I'm just going to paste
in some SVG code here. You can see here it's
not very common, but on some HTML
elements you might have a dash in-between two words
for an attribute name. Now, this is not
going to work in JSX. When you see anything like this, all you need to do is
convert it to camelCase. I'm going to just remove the
dash and capitalize width. Now if we run this, I don't know if we can
run our standalone JSX, let me just copy that
into our app here. If I put that onto here, you can see it works. But if we were to use a hyphen, we're going to get
invalid DOM property. Did you mean this? That's because we can't have dashes in attribute
names in JSX. That's one difference. The other difference
you'll see right here, instead of using class, we have to use className again
in this camelCase format. The reason why, is class is a reserved keyword
in JavaScript. We can't use class here. That's one other difference. The third difference
is if we go back here, you can see we've got
some numbers here. We can put JavaScript
expressions into these attributes in JSX. If I were to take away this, it's 100 right now. Actually, we want to see this
rendering on our screen. Probably not a good idea to
put that as a separate file. Let's just put this
back in here so we can actually see it
rendering on our screen. Indent a little bit here. We can actually put some
JavaScript expressions in here. What we can do is
instead of 100, other really simple one
for you will put in some brackets here. What is 100? Is 50 times by 2. Here you can see you
get the same result. We can put a JavaScript
expression here. Obviously, we can put
in a variable for this. If I was to go const
x equals 50 times 2, and then we can just
slot x in here. Then if I was to times it by 4, you can see our circle.
Is it getting bigger? The height should
be getting bigger, but we need to maybe
create a bigger width too. Not sure what's going on there. But you can see if
we check that code, if I inspect it, you will see that the result
that comes through. It's a little bit tight
here. Here we go. You'll see that the
result that comes through is indeed 500 for the height, which is 5 times 10. We're going to see this
quite often in this class. When we're using React, we're going to put through
JavaScript expressions and variables into
attributes, into props. That is one difference compared to HTML when we're
comparing it to JSX. Another thing I wanted to
demonstrate in this video was how this JSX code ends up being compiled into React functions that
then generate the HTML. It's not particularly important, but I like you
guys to understand what's going on
behind the scenes. Unfortunately, I can't show
you in this environment. Maybe later in the
class when we put this project or our own
project into a local host. But if we go over
to JSX in-depth, on the React documentation, you can see if we
learn more about it, what's actually happening
is this code right here will be compiled
into React.createElement, and then you'll see
all of this code. What we can do if we wanted
to is instead of using JSX, we could write code like this. That way we don't
need to compile JSX. But obviously, this is
much nicer than writing out a bunch of React
methods like this, and especially when
you do nesting. If we nest it a whole bunch
of elements within elements, this would become
super messy and almost impossible to work with. It's very handy to use JSX. But I just wanted to
make note that this is not actually what
makes it to your browser. This is what makes
it to your browser, and that is what creates these elements in your
React environment. You can also read about
introducing JSX here. You can learn all about JSX on the official
React documentation. Again, I encourage you
to do this as we saw, JSX is an expression too. This is going to show us
how we can insert values within our JSX,
specifying attributes, we can use variables and other data rather
than just putting it in as a string rather
as we would in HTML. Zooming out from JSX now to talk more about React in general, I'll remove that variable because we're not
using it anymore. We talked about
the inputs before. We're going to be
importing React as we need in both of these JSX files. But then in order to
create the virtual DOM, we're going to use
React DOM here. In our particular
projects using hydrogen, we don't need to use React DOM. I believe that's taken care
of by hydrogen for us. But as you can see here, we're always going to
be importing stuff from React or from another
React library, which for this class
is going to be React. Then, you're going to see us
import stuff from hydrogen. Then what you got
is your exports. As you can see here, I'm
importing React and then I'm exporting this
functional component. Now, why am I exporting it? Well, I'm actually using
it in this index.jsx file. You can see here
that I'm importing that app component
from that app file. If I'm using it in this file, then I don't need to export it. But if I want to use
it in a different file then I'm going to
need to export it. Now if I just do export without
putting default after it. I can export multiple
different ones here. I need to specify which
one I am importing, and putting these curly
brackets on either side. These curly brackets here are doing what's called
destructuring. If I'm exporting multiple
components within one file, which even if I only
export one here, I'm not saying default. Therefore, I have
to go in here and pull that out in between
these curly braces. To show you the difference,
if I just go over here and because we've only
got one export, I can just put default here. Then inside here, I
can remove these, and it works the same way. But if I wanted to
say, for instance, create another
component in here, then I can't just do default. Let's just say I create an app2, and then so, whoops, wrong tab. I go over here and
then I can do app2. I can import multiple
from the one file. But if you're just
exporting one, then feel free to remove these, let's just get rid
of this one now, and then put default here. That's important to
note. You'll see defaults and not default used. You'll see the curly brackets and curly brackets
not being used. That's why sometimes you're destructuring and sometimes
you aren't destructuring. That's the imports and exports. Obviously, we saw inside
this return here is JSX. If I was to create another
function like I saw before, let's remove that default here. Let's just call this
a custom button. I can gain access
to the props of this custom button through
the argument here. What I'm going to do
is inside this return, I'm going to put in a button, same syntax as HTML because
it is a HTML element. Then I'm going to just say, click me, let's say. Then up here,
instead of this SVG, let's put in our custom
button in Camel Case. As you can see here, we've
got our little click me here. Now, maybe we want to
make this field dynamic. What we can do is I can
put the text in here. Then when I need to do is pull
the children of the props. That's going to pull whatever is in between these two tags. I can go props, children. There you go. You can see here, click
here, or click now, anything I put here is the
children of this component, and so I can access
it through its props. Now you won't see this usually, what you'll see is the
destructuring used again. Instead of props.children, what I can do is destructure
and take the children right off the props within
this section right here. Instead of props.children, I'll pull children off of the props and then
I can put it there. Then you can do click here. Then you could pass
in some other data. I'm just going to put
in an attribute here, let's just call it message, and I say, I am your message. Now we've got this prop that
we can access from here, and all we have to do is
pull that off as well. We're pulling off children, let's pull off message, and then I'm just going
to do a simple onClick. Then let's put in a clickHandler to find my clickHandler
here, clickHandler. Then I'll just do a simple alert with the
message that's passed through. Now if I click here, you'll see we get an alert
up here I am your message, and that is passed in
through our component here. The two props, we've got our children that
we can pass in, and then we can pass in whatever
props we define as well. We just need to pull that off
our props object up here, and then we can
use it however we want in our own
custom component. We went through
quite a bit there, but hopefully, you
guys got the gist. Again, we don't need to go
through a whole class in React in order to understand enough to start
working with Hydrogen. But if you are
struggling, of course, you can research any of this
stuff that doesn't make sense by going to the
React documentation, or just searching
for it on Google. Honestly, React is such
a popular framework. There's so much
information out there. If you have any issues,
just start googling, just start looking
at the documentation specifically with React, and of course, if you
do that and it fails, you can always leave a comment below with anything
you're struggling with. Before we move on to the other parts that you
need to understand before we start working
specifically with Hydrogen is, I just want to break down the three major
things we're going to be importing and
using from React. As you can see here, we have made components, but we can also import components
from certain libraries. That's what we're going
to see when we start to import components that are already created by Shopify
for us on Shopify Hydrogen. You'll see that very soon. But then, of course,
we can create our own components
like we did just here. Another thing we're going to
import from React and from Shopify Hydrogen is
something called a hook. If you want to go to
the documentation, there's an introduction to
hooks, somewhere in here. Hooks, here we go. There's
a whole section here, and you can read about hooks. Simply put, hooks
allow you to use state and other React features
without writing a class. That might be hard for
you guys to grasp, for those of you who haven't
used React for a while. But as you can see here, we can import a common hook
called useSate from React. Let's actually look
at this example. If I go into here, you can combine destructuring with the total object
itself. Here we go. I'm just going to grab useState, and so now I'm importing
that Hook from React. What we can do is
again destructure, we can grab the state variable and then the function
that sets it, and then grab that from useState passing in a
default value of zero. Let's just grab this part first. Now inside our component, we can grab a count
and a setCount method. That's all we need in order
to create this button. I replace my button with
this one right here. Now as we're clicking
this, the count is going up, but
you can't see it, so what I'm going to do
is put in here the count. I'm just going to put it
straight in the button, not put it in another element, and if I click this,
the count goes up. I just want to
introduce the concept of a hook very early
on in this class, because we're going to use certain hooks within
Shopify Hydrogen. The most common of which
is the useShopQuery, which is what we're going
to use to bring in data from our Shopify Storefront API and use it in our project. We've seen components here, and we've seen hooks. The only other
thing is utilities, which is basically any other
functions that we will use to make our life easier
when building storefronts. I'll give an example, I found this utility
somewhere online. It's just a function for
formatting currency. We have this utility in
Shopify Hydrogen already, so we shouldn't need to
use something like this. But as you can see here, we can grab this utility. Let's just paste it right
into our code here. I'm going to get rid of
this component code. This is written in TypeScript, so I just need to remove
the type definition there. Now you can see it works. Let's just say if we
were to do a price. Again, I can throw in
an expression here, and let's just say formatCurrency
and throw in 110.10. Now you can see here
because we are using the currency USD and
the style of currency, you can see now that currency
gets formatted nicely. If we were to add some
extra decimal points here, it still gets rounded up
to the nearest cents here, and then we put the
$ sign in front. If we were to make
this something like Great British Pounds, for instance, you
can see that it's now the pound symbol instead. This is what a utility will do. It's just a function that
makes our life easier. As you can see here we've
imported it or just grabbed it. We could, of course, create a new file,
and then export it, and then use it wherever
in our project, which would be the
more likely scenario. But I just wanted to
demonstrate what a utility is, because we might use
some utility functions in our Shopify Hydrogen
storefront as well. That's really it, guys. You can, of course, go
way deep into React, but for this course, that's all we really need
to get started. All of these concepts we just discussed are
going to help us generate the UI for our
new custom storefront app. But the other side of this is
going to be how we actually connect this UI to
the storefront API. This is exactly what we're going to cover in the next video.
4. Storefront API: In this lesson, we're going to talk about the storefront API, which is what we would use
for retrieving the data required to populate
our storefront with all the product, page and blog post information we want
to show to the user. To provide a little
context here, the Shopify API is one of the few APIs
provided by Shopify. The two most common, I'd say, are the storefront API
and the Admin API. The Admin API is what you would use to complete admin functions. Whereas the storefront
API is the API for retrieving customer
facing information. I've talked about
the storefront API in a few of my other videos, you might have
seen me connect to the API and less than 11 of my Shopify theme programming
class and for a minute, 1820 in my YouTube video using JavaScript in
theme development. However, as I mentioned
in those videos, the storefront API was not
built for Shopify themes, instead, it's intended for every other scenario
where you would need to access storefront data. As Shopify say in
their documentation, it can be used anywhere
your customers are, such as a website app
or video game and now this includes
Shopify hydrogen apps. How does it work in
Shopify hydrogen? Well, the good news
is that hydrogen has a built-in hook which helps you easily connect to
and query data. It's called the use sharp query hook and later in this
class we'll set up our access details in
the hydrogen config file and then use the hook to pull
in whatever data we need. For now, what I want
to briefly cover is the syntax of GraphQL, which is the language we use
to query the storefront API. This right here is
called graphical. It's a graphical
in-browser GraphQL IDE. That might sound
super technical, but basically all this is, is a way for us to
create and test out queries before we
run them in an app. I'll show you how
to set this up for your particular project later, but for now you can see I'm
running a local server. This is actually a hydrogen
project and luckily for us, one of the features
is this graphiql. It's basically GraphQL with an i between the
graph and the QL. Graphiql, I guess it's
how you could say it. We've got this tool built-in
and all we have to do is, if this is our root
domain local host 3,000, we just need to put
graph iQL after it, and then we get access to this little tool that
allows us to run GraphQL queries to whatever storefront API hydrogen
project is connected to. This one is actually
connected to the hydrogen preview store. This is the standard
store that will load into your storefront API
configuration settings when you create your first
hydrogen storefront, you can update these settings, will see that in a later video, but in this class we're
just going to use the hydrogen preview
store and that has a whole lot of data
already in it so we don't even have to create
sample data for this class, we can just use what is already provided for us from Shopify. We've got a query here already, if I click "Run" here, you can see that we get a data structure back that mimics what we write over here. I like to use indenting, so I'm just going to
put this over here. What we get back is some JSON that mimics the
structure of our query. That's what's cool
about GraphQL, it mimics what we get
back in terms of JSON. To explain this a little bit, we've just got a
basic query here, we're taking the
query root of sharp, and then we're opening
up a selection here and then we're defining what
fields we want to return. All we're doing is returning the name of the sharp,
which is hydrogen. But I could also add in
this field description, and then if I run that, you'll see that when
our query comes back, we've got our description
coming through as well, okay? We can add in whatever fields exist on the sharp object here, if I was to remove the selection altogether and try and run that, it's not going to work, it says field must have selections. There's certain fields in
your API that are going to require selections and
shop is one of them, so it's not going to work as requesting the whole object, we're going to have to request the specific fields
within that object. How do we figure out which fields we can access
on the object? Let's go to the documentation. If I simply search for
storefront API in Google, I should be able to find it. Here is the page on
the documentation, the graphQL storefront API
from Shopify and honestly their navigation is
a little bit hard to navigate but I want to
go into Comment objects, go into here and
if we scroll down, this is not what
I'm looking for, online store is the
category I'm looking for, I'm going to objects, we can see here the shop object, so we can see the shop
represents a collection of general settings and
information about the shop and here
are the fields, so we access the
name of the shop, the description, we can
also get Meta fields on it. We can get the primary domain, so if I wanted to
add that primary, the good thing about
using this interface is it's got auto-complete. I can throw in primary domain there and it
requires selections. If we wanted to use that, we just needs to check out
this which is the datatype. As you can see, there's
a range of selections in here too so I'll have to open up a selection on that one and then URL is one of
the fields on that. I can run that and
then now we can get the primary domain URL. Now, one of the trickiest
things about GraphQL, this is pretty simple
up until this point. But if we start to work with
relational connections, we're going to start
to see something called edges and nodes. What I mean by that
is if we go into here and type in something
that's a plural, we can't do it on a shop object, but let's go back
to our query route here and if I type
in collections, as you can see here, it's going to return Collection connection. Here we can target a
specific collection, but if I go here
and do collections, what I'm now going to need to do is use edges and nodes, alright? I'm going to have to access the edges on this
collections list and then grab whatever
data I want on each collection within that list of collections here under node. I can grab the, let's say the name with the handle rubber and
then if I hit run here, I'll probably have to put
it in the perimeter here. As it says here, you have
to provide first or last, so what that means
is I have to specify how many collection
objects I want to return, so I can request the first 10. I run that. You can see here that I get back an object that's similar
to the structure here, I get back ten collections
or maximum ten collections, there's only 1, 2, 3, 4, 5, 6, I believe, collections in our
store in total, so we get the first 10. If only six exists,
we get six back and because we've only
asked for the handle, we only get the handle
back on each node. Now, this edges, node thing is a
little bit confusing but you'll get used to it, the way I like to think of
it is we can get more than just the edges back on a
particular collection list, any list for that matter. If I go under edges here, I can also get an object
here called page info, which is a selection and then I can see if
it has a next page. If I go over here
and run that again, not only do I get the
data I'm looking for, I also get the information
for pagination, which is telling me does it have a next page, it doesn't. If I set this to three, so it's only going to show
me the first three and the second three and
I run that again. Now, if we look down, we only get the first
three results and we get true now
for has next page. Similar thing with node, we can go underneath the node and we can type in
something called cursor. Let's run that and
see what comes back. As you can see,
we've now got this little id here on every node, which gives us an address
for that particular node. It might sound
pretty complicated, why can't we just return a list, but it goes back to relational connections between
these different resources. Because these different
nodes are resources themselves and what we're doing is returning a
connection to them. It's not just data
within a collection. It's also product data as
well that could be part of multiple collections and
so it looks complicated, but all we need to remember here is even if we're not
using pagination, so if I remove these, just be sure to use edges and nodes when we're handling connections in
this list fashion. Before we move on to talking about the Hydrogen framework, I just wanted to show you a few more examples of what
you might deal with in terms of GraphQL while you're creating custom storefronts
using Hydrogen. A few examples you
can find within this tutorial that is provided
by Shopify, in essence, this class is a video
version of this anyway, we will use a lot of what gets talked about
in this tutorial, but we will also not use
a ton of it as well. I'm going to try and
keep it really simple. A lot of this tutorial I've
found is really jumping ahead in terms of
adding a lot of code, which might not make
a lot of sense. The class has been
designed to step through it a bit
slower for you guys. But I want to draw
your attention to a few GraphQL queries that
you may end up using. As you can see, we've got that
query that we saw before, where accessing name and
description on shop, so that's a very
basic query there. If I scroll down a
little bit more, I'm not sure if it's on this
page or on the next page. Let's go to build a
collection page, scroll down. Here, you can see we've
got collections here. We can actually skip
out the edges part, so we can just write nodes instead of writing edges
and then node singular. If I wanted to remove
edges here and run that, you'll see that we get
back the nodes without having to put in
that word edges, so that's an option as well. Then as you can see here,
we've got the parameter for gaining the
first eight eight, of a particular collection, passing through a handle, which we'll see
later in this class, and then we're grabbing the first variant
off of each product. Again, we'll do something
similar in the class. I just wanted to run
through a few examples, see if there's anything
theoretical that I need to tell you guys
before we get started, although this is quite similar if I go into the next page. Here's something that I wanted to show you before
we get started, is the use of these fragments. Fragments are like
if you're used to Shopify theme development, you can think of it
almost like a snippet. It's a reusable bit
of code that we can plug into other parts
of our GraphQL query. We define this fragment here, which tells which fields
we will want on media. This whole fragment
here tells us which selection
of fields we want and then we can plug
that in to where we need it by using
this dot, dot, dot. Instead of writing out all
the media fields here, we can just expand out the fragment that
we created up here. That's handy when
we want to reuse that selection across
multiple areas of our query. The other thing over
here that I want you to notice is this dot, dot, dot, and then on. Here, it looks similar in terms of you've
got the three dots, but as much different
in the sense that this is only going to return this selection when the
type is media image. It's going to return
this selection, when the media field that
comes back is a media image. It's going to return this
selection when it's a video. It's going to return
this selection when it's a 3D model and this selection when
it's an external video. I guess that's why it's created in a fragment so that we're not nesting all that code
within our selection here. I'm not sure if we're going to necessarily use
fragments in this class. We're going to keep
it very simple. But I just thought to
mention this while you're looking through
examples from Shopify, outside of this class, you may be wondering
what's going on here. Definitely look up
GraphQL fragments if you're interested to
learn more about this. Let me go down, see if there's anything else I want to share
with you guys. As you can see here,
guys, this tutorial throws in a lot of
code really quickly, which is why I'm
not following this exactly for this
Skillshare class. I think they jump into certain
parts a little too quick. While I don't like
to be drawn out, I do like to cover a
lot of the stuff in a step-by-step
sequence that allows you guys to understand what's
going on at each stage, so I feel like it's better
to go slower and actually understand deeply what's going on rather than jump
ahead so much. In my opinion, I
believe that they're jumping a little
bit too far ahead, especially for somebody
who hasn't used React before or is new to React, this tutorial goes and
jumps straight in, has a lot of detail. Obviously, a lot of this
is just React code. That's the main stuff I
wanted to show you guys. I wanted to show
you guys if I go back to here the fragment stuff. Let me just find that on the
documentation for you guys, if you want to have a
look at that later. Here you can see on the
GraphQL documentation, the official one at graphql.org. If we look at the
section on fragments, you can go more into details
here on what fragments are. That's the only
complex example I've seen in the Shopify
examples of GraphQL. We're probably not going to
use fragments in this class, but just in case you see that on an example like on Shopify documentation and you're wondering what's going on there. But we're going to try
and keep it as simple as possible and then try
and expand from there. But it's important to understand GraphQL at least
on a basic level, before we get stuck into
creating our projects, we need to know
how we're going to query the storefront API in order to get the
data required to build out custom storefronts. That's why we need to do a
little lesson on GraphQL. In the next video,
we're going to cover the Shopify Hydrogen framework.
5. The Shopify Hydrogen Framework: In this lesson, we're
now going to talk about the Hydrogen framework
ie the set of hooks, components, and utilities
that Shopify provides to assist us in creating
custom storefronts. For most of this class, I'll be explaining Hydrogen
hooks and components as we work through our
own Hydrogen project. But there are a few concepts
regarding Hydrogen I'd like you guys to understand
before we get stuck in. Firstly, if we head to the
App.server.jsx file here, you'll notice this file
is going to look quite similar no matter what Hydrogen project
you're looking at. You've got this renderHydrogen
function right here, which is responsible
for hydration. Hydration in this case is relating to server
side rendering. Basically, this is
the function that sets up your project
as a Hydrogen app. After importing
renderHydrogen up here, the other top level
components you are likely to notice relate to
routing and providing. In a typical React project, you might use something
like React Router, which is another library
for handling routing, but Hydrogen comes in
with built-in routing. Using the combination of the Router component and
the FileRoutes component, we can easily set up
basic routing within our application that mimics the file structure
of our project. For instance, in
our routes folder, if I click over here, if I create a catalog. This is what we'll do
in the class later on, but just to show you upfront, if I create a
catalog.server.jsx file, then I can access it by typing catalog after the route
domain of our application. If I run this; it's not currently running
at this point in time, but if I run the command
yarn dev or npm run dev, we're going to be running
that on localhost 3001. I'm running another
store at the moment, which is why it's at
3001 and not 3000. If I command, click that, you'll see Hello World here. If I go back into my code
editor and let's just put something in
catalog.server.jsx. Let's grab what we have in index.server.jsx and
just replace this to Catalog and replace this here with Catalog
with a capital. Hit "Save" on that. Switch back to our app in the browser. As you can see here, it's
running the index route, which if we look, it's just creating a div with Hello World so that matches. Then if I go over here and after our route
domain put catalog, now you can see the
word catalog coming up. This is how we get built-in
routing into our application. We just put in a router and
wrap that in FileRoutes, and so our routing will mimic whatever we put
in this routes folder. We can do nesting
via nested folders. It's actually quite easy to set up nesting with
Shopify Hydrogen. I can also do a dynamic value. Let's just say I had a nested
route for collections. I'll create a
collections folder here. Then I can also create
a dynamic route. I can use handle as a
variable, server.jsx, and then we're not
going to do it in this video because we'll have to bring in the use shop query
hook and start querying API, which we'll get into
in a later video. But essentially now I'll just
change this to Collection. If we did have access
to the collection, we can start to dynamically take the
Collection that we're looking at and maybe put
out its title or something. We can create these
dynamic routes as well. This will live at, let's just say our
collection was called shoes, so we just go to this route, collections-shoes, and then that shoes part
becomes the handle, and so it would serve us this
file through that route. I can also use the
routes component. It's not currently
imported but we can import the route component
from Shopify Hydrogen. What that allows us to do is
define a route down here. This is handy for creating a catch-all route for 404
full pages for instance. If I create a
NotFound component, I can load that in there. Then if the URL doesn't
go through FileRoutes, it can go through this
backup Route instead. We're going to see that
in this class as well. Then the other components,
you're going to see are the Provider components. As you can see up here, Shopify Provider is the top level provider component
from Shopify Hydrogen. We put that around the router
so that we have access on every page to core Shopify functions like
the use shop query hook, which we'll use in
virtually every route. This pattern of using a Provider component with
related components and hooks is something
you're likely to see in various parts
of your application. Examples include
the CartProvider for providing cart
functionality, the ProductOptionsProvider for
setting up states to track the currently selected variant
and option selected on a product page and
CartLineProvider, which provides easy
access to data relating to an
individual CartLine. These are just a few
examples of some of the available components in the Shopify Hydrogen framework, the full list of
which you can find here on the official
Shopify documentation. Also as a tip, if
you ever feel lost on what a particular
component or hook does, you can simply just
Google Shopify Hydrogen, followed by the name of
the hook or component, and that should
usually direct you to the relevant page of the
Shopify Hydrogen documentation. Remember hooks generally
look like this with a lowercase first letter while components all start
with a capital. This is to differentiate
them from regular HTML tags.
6. Getting Started with Hydrogen: Without further
ado, let's actually get into building our
hydrogen storefront. We've talked a lot of theory, it's time to get into
the practical here. There's two ways we
can get started. Number 1, you can do
it how I did it which was following this
documentation here, shopify.dev/custom-storefronts/hydrogen/ getting-started/quickstart. Basically, just look for
this in the navigation. Here, you can see in Step 1
we can create a Hydrogen app. I was using NPM until I ran across this caching issue that
forced me into using Yarn. We'll talk a little bit
about that in a second. Now I use Yarn, but you can also use NPM. It's important to note here that when we're
using these tools, you need to have them installed. You need to also
ensure that you have the latest version of
node installed as well. This is going to
come up every single time we use package managers. If you're not familiar, node.js is the
JavaScript runtime, we need this, this is at the core of NPM and everything we'll
do in this project. Make sure you have the
latest version of that. Then NPM is Node
Package Manager, it sits on top of that, and it helps you to install
and run node packages. We need to make sure we've got the latest versions of that. If you want to use Yarn, you can install Yarn as well. The details for
installing all of these command line
interface tools are on their
respective websites. I'm going to show you how
to do it this way first. I'm going to open up my
favorite terminal application, it's called iterm. I'll maximize this for you, so you can see easier. Clear all that stuff there. I'm going to simply navigate
to my desktop for this, so I'm going to cd desktop. Then let's run these commands. Let's just run NPM
init, Shopify hydrogen. It says you'll need to install
the following packages. Yes. Just need to give it
some time to process here. Now here, it's going to
give you the option to do Demo Store, or Hello World. Demo Store is a more
full-featured hydrogen project. I don't recommend installing Demo Store in order
to learn hydrogen, because it comes with a
lot of code and a lot of setup that if you're
just learning hydrogen, it's better to keep it simple. I like to go with Hello World. Then, here is a
matter of preference, if you don't know
what TypeScript is or don't use TypeScript, then just choose JavaScript. I'm going to call this
custom storefront. Now it's going to download the code from the
hydrogen repository. Now after a fair amount of time, it has finished installing. Now, I'm going to
open up a new window or Visual Studio Code, and we can start to
run this project. I'm going to open
up a new window, will just resize it
to fit your screen. Then I'll click "Open", head into our desktop, and open up that custom
storefront folder. Here, if we run a terminal
within this project, we can do this from within the project on
Visual Studio code, or we can do it using our
other terminal application. Is just easier within our
code editor because we've already navigated
to that folder. Now all I need to do
in order to run this, is npm run dev. As you can see here, it's going to say that we're now running at localhost 3,000. I'm going to command-click
to open that up. As you can see, all it says is Hello World. The reason why it says
Hello World is if we go into our src
folder right here, and head into our index route. Just in here in routes index, you'll see Hello World. Remember we already talked a
little bit in the last video about what's going on the
app.server.jsx file here, so I won't go deep into this, but this is the heart
of your app right here. We've got our Shopify provider, which provides basically
all the functionality that we will need for
our hydrogen app. Then of course we're pulling him router and file routes,
for our routing. This other component that
we're importing from react, is a experimental feature in
react at this point in time. If we look at the page for
suspense on reacts website, you can see here that
there's a lot of red here. Basically a suspense component, it allows you to wait
for some code to load, and declaratively
specify a loading state. This allows us to
do asynchronous loading within these two tags. We could instead, and let
me just do this right now instead of having a null
full back, just to loading. Now what I've done to get us started so that we're
not wasting time here, is I've created all
the CSS for you, so that we're not writing CSS as we code
through this project. I've also updated the
index.server.jsx route, so that we can see the pattern that is going to happen in a lot of the routes, where we input a bunch
of components and hooks, we export a
particular component, and then below that,
we write our query. What you can do, this
is the second option, is you can get clone
the storefront project, that I've uploaded to my GitHub, this is a public repository. This is basically the same thing as if you were to do what we did here in our terminal with creating a new
hydrogen project, and choosing Hello World, except that I've updated
this index.server.jsx file, to demonstrate the
ongoing pattern that we're going to see
in a lot of routes. I've also added a
bunch of CSS here. I'm also going to update the
branches as we go along, so you can see the
end point as well. I encourage you, instead
of doing it this way, I encourage you to get clone
this project, run npm, install, and then let's
follow along together, using the exact same project. What I'm going to
do, leave that, and then I'm going to, let's just close out of this. Then in my iterm here, I'm going to navigate
to my code folder, go into my Skillshare folder, which is where I put my
Skillshare projects, and let's run git clone here. I'm going to go git
clone, copying that URL. All I got to do is click
on the screen button, and then copy the HTTPS address, and then now it's going to download that repository
to my computer. Let's navigate into
hydrogen class storefront. Then from here, we
just run npm install, to install all our node modules. This is something
that's happening automatically when we run that previous command of
npm init Shopify hydrogen. It runs npm install for you. When we're downloading
a GitHub repository that said node project, it's going to get ignore
the node modules file, because that's not code that's
specific to this project, and it just stores the references in the
package.json file. Then when you run npm install, it will then install
all the dependencies. Just a bit of extra context
there for those guys who aren't too familiar
with node projects. There we go, we've
installed our dependencies. Now, I'm going to open up
Visual Studio Code again. Get this to our screen
size for you guys. Then I'm going to
open up that folder. Going into code, Skillshare, hydrogen
class storefront. Removing that tab there. You can see if we go into the
SRC folder, in index.css, we've got all the CSS that
I've already written for this project and then if we go into our index.server.jsx route, we've got this right here. Let's go ahead and run a new terminal directly
inside our project. We're going to clear all of this annoying code
that comes up. That's just a me thing.
Don't worry about that. Then let's run our command
for running the server, npm run dev, and then we
will open up localhost:3000. I'll get rid of
these other tabs. Now that we already got
our project locally, and here you can see we have
a title and a description. Now, where's that coming from? I wanted to update the
index.server.jsx file to show you a pattern that we're
going to be using throughout virtually
all of our routes. How we're going to run through this project is we're going to create a section at a time. We're going to do
the catalog page. we're going to do
the collection page. We're going to do
the product page, and then we're going to
enable code functionality. Basically, the pattern
that we're going to use, and if you can understand this within Shopify how
to do development, then you're well on your way to coding any type of project. The pattern is this. We're going to import what we need from
Shopify hydrogen, we're going to
export a component, and then we're going
to create our query. Now this is out of order
because we're using the query up here in our
useShopQuery hook. But for whatever reason
in the examples, Shopify puts the definition
of their query at the end, so I'm going to
follow that sequence. I'm just going to minimize
that for a second. Hold on, that maximized it. Let me see if I can just create some more space here
so that we can, no, that creates a new tab. Maybe what I'll do is
I will put that away for a period because
I want you to look at what's going on
here in its entirety. Let's remove that so we
can get a closer look. What's happening here is if you remember in the previous
index.server.jsx, it just had a simple component that had a div with
hello world in it. Now that's not going to
be particularly useful because that is not
dynamic content at all. But what we're going
to be doing in this class is we're going to be bringing in dynamic
content from your store. I'm not going to reload the page here because I
already shut it down, but here, you can see we've
got hydrogen and then we've got a custom storefront
powered by hydrogen, Shopify's react-based framework
for building headless. Where does that come from? Because if we look inside here, you can see that these
are both dynamic values. What we're actually
doing is we're pulling that information from
the storefront API. The three things that we
import from Shopify hydrogen here is the all-important
useShopQuery hook, which is what we use to
query the storefront API. We're bringing in this object
here called CacheLong. This is just a
caching strategy so there's nothing too
complex going on here. We can easily replace this by writing out
the caching strategy. It just helps us a
little bit saving time by bringing that little
utility out of hydrogen. Then this right
here is a utility that helps us to do
syntax highlighting with our GraphQL queries
although it doesn't really look like it's working in my particular setup,
but that's fine. We don't really
necessarily need that, that's just a nice to have. What we're doing
here, so we've got our home component right here that runs on the index route, and in our return
statement here, we're returning JSX which we
saw earlier in the theory. I've already set
up a class here. Again, this is not going to be a CSS course so I'm
going to just put in class names that match up
with the CSS that I've built so that we can get some
styling from the get-go. All we need to do
is make that match up with our CSS file here. But again, you can totally
edit this to however you want. I just didn't want to get
caught up on any CSS in this class because this
is not a CSS class. Getting back to our
index.server.jsx file here, what we're doing is
we're passing through an object to useShopQuery with our GraphQL query
allocation strategy and then we're saying
preload: true. This is just a
basic default setup that you can see in
the Shopify examples. The main thing to
understand here is that we're writing our query here in GraphQL and then we're putting it
into useShopQuery. Now in the examples, what you'll see is object destructuring
happening right here. It doesn't have this line, but I like to break it out, or at least for this class because it just makes it a
little less complicated. You can break down the
steps a little bit. What we're doing is
the data that's going to be returned from
the API is going to go into this data object
here and then we're going to destructure it in order
to get the returned data. Now, here's something
you can do throughout this class that will help you determine what comes
back from the API. You can go console.log (data). Now, where do we find
this information? Well, we're actually on a
server-side component here. As you can see over here, I don't believe we have any client-side
components just yet, but as you can see here, all of our components so
far have.server in them. If we run this, the console log is
going to happen in our terminal but
not in our browser. Let's open up our
terminal again, get rid of all that, and then let's run npm run dev. Now we're just going
to have to hit this. I'm going to refresh the page and if we go back to
our console here, I'm going to open
this up and you can see the data
that comes back. It's actually coming
back twice here. You can see we get this data object and inside
we get the sharp object, and then inside that we get
the name and the description. This will allow us to
inspect what's inside. As you can see, this destructuring matches what comes back from
that data object. We can remove this and
instead of having Shop here, we can do data. data. shop. Refresh the page and you see we get
the same result. But obviously, that's
a little bit uglier. This might look a little
bit more complicated, but it helps us to pull off that sharp objects so that
we don't need to put data, data in front of it. That's essentially
what's happening there. I remove that console
log and you can see that we're pulling off
the shop object off of the return to data
and then we can then access the name and the description
because that's what we requested in our GraphQL query. That's the basis of everything we're going to
be doing in this class, we're going to be
importing components, hooks utilities from
the hydrogen framework. We're going to be using those hooks to plug
into some data. We're going to be using that data here in
our components, and if it requires it, we're going to write
queries that we will plug into in the case of useShop query in order
to get our data. If you understand this pattern, then we're well on
our way to coding up our first-ever
hydrogen storefront.
7. Creating a Layout Component: In the last lesson we got
started with our project, we got it set up and running on our computer. You can see here. One of the things I
forgot to mention in the last video is that this data is coming from
the Shopify preview store. If we go into our explorer here, and go down to, I believe it's
hydrogen.config.js, you can see the storeDomain is hydrogen-preview.myshopify.com, and the storefrontToken is what we need in order to
connect to that. That's why we've got this
data right out of the gate. In this class, we
don't actually need to create a store or
update our store with demo data because
we can just use this demo store the
entirety of this class. However, when we deploy this to our actual Shopify store, we're going to want to
replace these details with our own store domain and our own storefront
access token, okay? I just thought I'd mention
that before we get started. Some of you might be wondering, where are we getting
this information from? Well, it's pre-loaded to use
the hydrogen preview store, which I think is very
nice in this class. For instance, we don't want to have to create all
this demo data, we can just plug in to existing demo data which
is very convenient. With that out of the way,
what I want to do in this video is create
a layout component. Our layout component
is going to wrap around our entire website
and basically provide a header and necessary SEO that will run on every route in
our hydrogen storefront. What I'm going to do is, I'm going to go into
the SRC folder here, create a new folder
for my components, and then inside this
components folder, I'm going to create a
layout.server.jsx component, okay? Now, this component is going
to serve two functions; it's going to render a header, and also render SEO
information so that any page on our website has
SEO already built in, okay? Let's just, to make
our lives easier, I'll just copy everything in here and put it over here, okay? Now what I'm going to
do is, remove that, and let's just put a div in here to start with so that
we don't get an error, and then we'll name this
not home, but layout. Then as I showed you
in the theory video, I'm going to use object
destructuring to grab any children that get passed into this layout
component, okay? Same query here, we're going to use the name and description for the header and also
the SEO information. Then what I'm going
to do is in our index.server.js file is
simplified as a ton, we're not going to use
this data on our homepage, so I can remove all of this, I can remove this up here, and we're just going to return
a very basic component, and in here, I'm going to
just put homepage, okay? Get rid of that. Then
what I'm going to do is bring in that
layout component. I can import, and I believe we're
exporting default over here, so we don't need to use
the curly brackets, we can just do layout
from components, and then we just go to create a path to the layout component. Then what I want to do is
pass in all of this in between our new layout
component tags, okay? Now, as you see here, we are accessing the children, what I want to do here is put that children in
between these divs, okay? Let's see if that is
working now for us. If I go over here, you can see, yes, there are no errors, okay? Going back to here, what
we want to wrap around this basic homepage is a
header and SEO information. Let's start working on what's inside our return
statement here, our JSX. First of all I do want
to return whatever gets passed through
those layout tags. What I'm going to
do is call this main because this is going to be our main part of our page, and then I'm going to wrap
children in a couple of suspense tags because
we don't know what sort data could
be coming through. Remember this is going to
wrap around every route, not just the index.server.jsx
route, okay? Now, we're going to get this error here
and that's because we haven't imported
suspense yet. I'm going to import
suspense from react. As you can see, it just
filled that out for us. Now we are bringing
in any children whether they're loaded
asynchronously or not, okay? Now all we got to do is bring
in our SEO and our header. Now, the way we do SEO in
Shopify hydrogen is to simply import an SEO component from the hydrogen framework, and then what we can do is bring in that SEO
component over here. We don't need an end tag, but we do need to put it
in that dash to close it. Then inside here, we'll put
in the type of default SEO. Then in the data field, we'll put in an object for
title and description, and this is where we'll
use the shop name and description that came back
from the storefront API. We can put that in here, shop name and shop description. Now, what you'll notice
here after we've done this is that we're getting
all these red squiggly lines, and that's because
we can't return multiple elements on
the top level with JSX, so we're just going to have to create a top-level element, and we can create one
of these blank ones, like this, little bit of a hack, but now we don't get
those red squiggly lines. If I go back here, re-run our homepage, and
I open up my dev tools. I just pressed Option
Command I to do that, otherwise you'll have to go into your menu and open up DevTools. I do this in every class, so hopefully you guys
are following me here. Then if I go into
our elements here, you can see that if we go
into our head tag here, you can see that we've got our title and description coming through in
these meta properties, which is our SEO tags, okay? We got our SEO
coming across now. Now we just want
to build a header. Again, I'm going to
leverage some of the classes I've already
written in the CSS, this is not a CSS
class so I'm going to grab this container
header-inner as a class. Then in here I'm going to create a link so we can use the
link component in Shopify, and I'm going to link that to the homepage by just
putting in slash there. This link is going to be
basically the header logo, it's a common pattern with headers that if you
click the logo, it will direct you
back to the homepage, so that's exactly what
we're doing here. Then in here is where I will
put the shop name, okay? Now we need to import link from our hydrogen framework because
we haven't done that yet. Then if I hit "Save" on
that refresh over here, you can see we've
got this header now. Then if I click on this, it'll take us to the
homepage which we're already on, okay? That's going to come
in handy later. Now what I'm going to put in is a unordered list with the
class of header navigation. This is going to house our
header navigation obviously. Then I'm going to use some Emmet here to put in three links. Just going to have empty
links in this stage, link 1, link 2, because we haven't
got any other pages, so I'm just going to put in
some placeholders basically. Then finally we're going
to have a spot here to link to the cart page. We don't have that yet, so what I'm going to do is just put in a placeholder here. The class name of it is going
to be header-cart-link, so I'll just put
that in there, okay? I'll hit "Save" on that. If we go over here, you can see we've got our header and everything is
nice and centered. Basically, what we're
going to do guys is as we go
throughout the class, we're going to update
our header as we go. As we create new pages, we can add in links
to those pages here, and then we can add in our cart icon here with the number of items
in our cart as well. We'll get to that
later in the class. But now that we've created
this layout component, you're going to wrap
all your routes with this layout
component tag here, and that will ensure
that the header and SEO information will
appear on every route. With that out of the way,
let's actually create a catalog page for
viewing all of our products on our Shopify
hydrogen storefront.
8. Building a Catalog Page: In the last video, we created
this layout component, which is what we're
going to use as our standard layout
across all our routes. We updated our index
route and now we have our super basic homepage
sitting within our layout, which then gives us our
header and our SEO, which we saw in the last video. In this video, what
we're going to do is create a new route. I'm going to call this
the catalog routes, sorry, that's the wrong folder, right here in our routes
folder, catalog.server.jsx. Just as a little bit
of context here, the reason why it's
a.server.jsx file here is because
we're going to be querying the storefront API. It's a good idea to
every time you query the storefront API in every router component,
that you're doing that. You want to be doing that as a server-based or
server-side component, that way we're not exposing
what we're querying, essentially database,
to the end user. Let's start off by exporting
a functional component here. This is going to be
our catalog page, which is going to house all of the products that
exist on our store. I'm then going to put in a return statement right
here and then we're, of course, going to start
with the layout component. We're going to have
to import that, otherwise, we're going
to get an error. So import layout from navigate to the
path, to our layout. Just so you know, guys, this dot-dot means go back
one in the SRC folder, and then we can look in
components and then find layout. If you're not familiar
with this right here, that's what that means. We're going to be drawing in some asynchronous
content here, so I'm going to put in a
suspense right after my layout. We're going to have to import suspense from React as well. Then a little bit of structure here that
I've already set up, I've created some CSS
for the classes of catalog page and container. We're just going to put our
page in a nice container. Then we're going to
create a div with the class of products
grid and that's going to house all of our
little product grid items. If I hit "Save" on that, we're currently not
pulling in any data, but if I go over here
to the catalog page, we've got a pretty
much blank page. If we go and inspect this,
you will see, however, that we've got those
divs that we set up, the catalog page, and the product grid. This is basically useless
until we bring in some data. Let's write our query
down below here. I'm going to create a constant called query
and then I'm going to use GQL string template
and put in our query here. Because I am using this, I'm going to have to
import that from Hydrogen, so I'm going to do that up here. Import GQL from
Shopify Hydrogen. Actually, let's do this in
our graphical interface. Then once we've tested it, know that it works, we can
bring it into our project. I think that's a bit nicer. I'm going to go to
graphical here. We can close down our DevTools. I'm going to get rid of this standard query right
here, open this up. Then what I'm going to do is we can name our query first up. It's a good convention
to do that, so I'm just going to
create a named query. I'm going to call
it query products. Then here is where we actually start to
request some data. I'm going to request products. It's going to error unless I
determine how many I want. I'm going to ask
for the first nine. Then I'm going to
open up a selection in here and I'm going to grab all the nodes and the
items that I want on it. Let's start with
title and handle. Let's run that. It says query does not exist on
type query route. That's because I've
got to move this. I shouldn't be nesting
that in a object there. As you can see, now
it looks a lot nicer, there is syntax highlighting, so I think we have
solved that issue. Let's just find out.
There we have it. We are able to get back the first nine products from the store that
we're accessing, which is just the
Hydrogen preview store, and we can get
back the title and handle of all these products. Let's start with
that. Let's move this back into our project. Try and indent it
nicely here, like that. Then we need to bring
in use sharp query. We're importing that from
Hydrogen, of course. Then we want to go inside
our component here. We can basically
just copy the same, but use sharp query. Now we do over here. From the layout.server.jsx
file, put it in here. Instead of sharp query, it's just going to be query. We can name this
whatever we want. We could name this
catalog query, but I'll just call
it query for now. Then what I'm going to do
is let's just console log the data that comes back so we know how to D-structure it. Let's have a look here. We've got a bit of an error. CacheLong is not defined, so we need to make sure
that we're importing that from Shopify
Hydrogen as well. CacheLong. Let's see if this works
now. Let's go back. Open up localhost 3,000 in
a new tab and let's open up our dev tools again
to see if there's any client-side errors.
We got an error here. It says failed to connect
to the storefront API. This might be due to my
Internet connection. I forgot to put a C there, so that's probably
what the issue is. Let's refresh over here. Now we just need to go
into the catalog route, and now there are no errors. There is some error here. But before we saw that error, you can see here, might be a little
bit hard to see, but we are actually
console logging out the data object that comes
back from use sharp query. You can see here, we can use this as our
format for D-structuring, we can grab nodes within
products within data here. I'm going to do that.
Let me push this up again so we can copy
what we have here. Just like we did here when we D-structured and we got
to that sharp objects, what we can do here, if I copy that, we're going to go
into data once again, but then instead of sharp we're going to
go into products. Then within products
will pull off nodes. Then if I run nodes as
my console log here, let's refresh our catalog page, then if I look in here you can see all the nodes returning. These are our products. You can see it's
returning a few times, but you can see an
array of nodes. We're getting back the title and the handle of each
of these products. Then what we can do is
we can use that in here. I could go nodes. Here's where instead
of running a loop, what we're going
to do is run map. I'll explain why we do
that in just a second. We're going to run map. Then what we're going
to do is pass through each product through some JSX. I'm just going to put in
a basic div here and then what I can do is throw in a JavaScript expression
into this JSX. I can do the product
title right here. Actually, what we'll do is
let's make this a list, so I'll make an unordered lists. This is not going to
be the final form, but it'll just look nicer
for this basic example. Then I'll hit "Enter" on that. There is a syntax error here. I believe we need
another bracket there. Sometimes when there's
a syntax error it will stop your
server completely, so we need to rerun the
server, npm run dev. Now if we go to
our catalog page, you'll see that we have an unordered list with all
of our different products. Well, at least the
first nine of them, which is what we
requested right here. Now to explain this map
thing here, basically, we want to return all the nodes, but we need to wrap each of
these nodes in some JSX, hence why we're using map. If we just did nodes by
itself without any JSX, let's see if that
returns anything. We can't just return
a random array. We need to actually convert
that array into JSX, so that's why we're
using map here, which basically is going
to create an array of JSX. That expression
will be inserted in between these
unordered list tags. Hopefully, that
explains the use of the map function
there in JavaScript. We've got a basic list of all
our product titles below. The first nine, at
least, at the moment. But what we want to do
is expand upon that. Showcase the price,
showcase the image in a nice little grid as you would expect on any e-commerce site. What we're going to do is create a component for this
product grid item. What I'm going to do is go into my components file
here and create, I'm going to call it,
ProductGridItem.server.jsx. Then what I'm going to
do is the usual export, a default function, and so our component. I'm going to simplify it inside the actual component itself
and call it ProductCard. Then obviously we need a
return statement for the JSX. Let's just be very simple
for the time being and move this list item into
our component here. Now we're going to try
and access product, which means we need to
parse that in as a prop. I'm going to pull the product object off the
prop of this component, and then on this side, we need to import this
ProductGridItem or ProductCard, as we've called it here. I'm going to first
type it out here, ProductCard and hit
"Save" on that. Sometimes it imports it
automatically for you, but it looks like
we're going to have to do it ourselves this time. I'm going to import
that ProductCard. As you can see, the rest was filled out for us,
which is very nice. Let's try and rearrange
this a bit nicer. I'll put my components
that I create myself after my imports from react
and hydrogen like this. Then what we're going
to do is create a prop called product and
parse in that product objects. Now if we go over
here and refresh, you'll see we get the
exact same result. Now we're just encapsulating this code into its
own component. Of course, we need to
export that and then import it up here and then
we can use it down here, parsing in each product
into its props. There's a lot of stuff
we're going to do inside this ProductCard. Let's remove this UL because we're going to create this as a
grid, not as a list. So I'm going to get rid
of that UL component, update the nesting, and then over here what
I'm going to do is create a div with the class
of product-grid-item, which again, this
is existing CSS that matches what
I've created in here. Then what I want to
do once I'm inside here is obviously
bring in the title, but I also want to bring in
the image and the price. Now when it comes to images
in Shopify Hydrogen, Shopify have this amazing
component just called image. If I just do import the image component
from Shopify Hydrogen, and then if I add the
image field over here, actually, let's do
this query back in a graphical
interface over here. Then if I find what the field
is that I'm looking for, this is the handy part
of using graphical. I've got auto-complete here. If I forget what the name of the image field is on
this particular node, it's going to
auto-complete it for me. Then if I try and run that, you'll see that it errors because I have to
have selections here. This is going to return an
image connection, I believe. If I hover over it, it's going to return an image. What we can do is look
up the documentation to see what fields are
on the image object. I'm here at the
storefront API reference. Let's go into online
store, go into objects. Now I believe it's
in common objects. Here's the last place you look. Here you can see
the image object, and then here you can see, to get the URL of the image, we can access it like that. We can get the altText, we can get the width. What we could do is go
in here, grab the URL, and then we could just create our own basic HTML image
and bring in that SRC. But then we wouldn't get the benefit of
responsive images. What we can actually
do instead is use the image component, if I go over here, from the
Shopify Hydrogen framework. I'll show you how that
works in just a second. But first, we need to flash
out this query right here. I'm going to grab
the altText as well. I'm going to grab the height and I'm going to grab the width. Let's just run this, make
sure there's no errors. We've got the title handle and data relating to
the featured image. I'm going to grab
that and paste that back over here inside our query. There we go. The nesting
is a little bit off. Tap it in like that. Then so what I'm
going to do here is open up an image
component right here. How this works is we
can put in inside Alt, we can access the
featured image objects on the product and
then grab the altText. But then for the rest
of the image data, we can just parse in
through this data prop, the image object itself. I just need to close that. Then if I hit "Save"
on that and we refresh over here in
our hydrogen app, you can see all of our
images come through. If I right-click
and inspect them, you can see that we've got
lazy loading built-in, and we've got all these
different responsive sizes based on the screen width. In Shopify Liquid, you can do this
optimization by creating a snippet and using the
image URL filter to create different image
URLs to serve based on the dimensions of the image
within your Shopify store. But here, all we
have to do is use the image component
within Shopify. To show you the
alternative, for instance, if we just did an image tag with SRC and then we could just simply parse in
product.featuredImage.url, save that, refresh over here, you get the same
result, but you see how it's loading a bit slower. Then if we go in here, we see we've only
got the one SRC. For responsive images, we don't have to go through and write all the code ourselves, we just need to use this handy image component
provided by Shopify Hydrogen. As you can see, it's already
reloaded and you can see now we've got lazy loading enabled and all
these different URLs automatically generated for us. That is the power of using some of these components
that come through hydrogen. Some of them unnecessary
and some of them just make our lives
so much easier. We'll see that when we do product options and
we do cut as well. Maybe we could do that
without these components, but they just make
our lives so much easier and I hope you guys
are starting to see that now. What I'm going to do is wrap
this in an image container. This is just some CSS to get it showing up exactly
how we want it. We're running out of space
here, so I'll open this up. Then underneath the
image container, I'm going to put
in a div here with the class of
product-grid-item-title. Then here I can put
in the product.title, and then let's hit
"Save" on that, refresh over here and
you can see we've got the title of the
product and the image. But usually on a product
grid you want to see the price as well so
let's do that next. We're going to need to bring in some extra data for that
because currently we only have the title handle
and featured image. Let's go over to
graphical again. Then underneath featured image, if we type in
something like price, which is going to
get the price range and the compareAtPriceRange. Something that's a bit more
accurate is to actually dive into the product and
grab its variant. You'll see we can access price range and
compareAtPriceRange. But if we want to get
a specific price, what we can do is dig
into the variants. What I'm going to do is
I'm just going to look up the very first variant. Of course, this is a selection
with nodes within it, so we're going to have
to open up nodes. Then within variance,
I'm going to grab, let's see what
comes up for price. We've got priceV2, and that is actually
a selection itself, so we have to go in and grab the amount and the
currency code. Then if we want to have the
compareAtPrice as well, we will need to do
the exact same for that as well currency code. If I hit "Save" on that it
will not save but run on that. We look down here. You can see that for
this hydrogen snowboard, the price is $600, but it actually has a
compareAtPrice of almost $650. Obviously this works, I'm going to copy that. Paste it back into our project. Unfortunately, I think we're
going to lose our nesting. No actually doing okay
with the nesting. Hit "Save" on that and
then over here what I can do is create a new div here. I'm going to use the class of product-grid-prices
and in here, I can do product.priceV2.amount to make it real
simple to start with. What have I done wrong here? This is not matching up to
what I've got here products. I forgot to go into the variant. We're going to go into the
variance and go into nodes, grab the first node. That's a spelling
mistake, variants.nodes , price to amount. There you can see we've got
the price coming through, but it's not formatted.
What are we going to do? Well in Shopify liquid
there's a filter for this, which formats the
price nicely for us. We've got a similar thing within Shopify hydrogen called
the money component. I'm going to import the money
component right here and then let's take away that and then open up a
money component here. Then just like we
did with the image, I can put in the data
prep that same price, but I don't want to go
specifically into the amount. This component is
going to figure out what the amount is and what the currency is and format it nicely based on those two. I've made a mistake here. I've got two curly brackets, and that has stopped my server. Let's run the server again, run npm run dev, and then rerunning our
catalog page here. You can now see we've
got the currency symbol, and these prices are
formatted as currency. We've got nicely
formatted currency here. Now for completeness sake, let's actually build in the
compare at price as well. What I can do down here, let's just do this
to get started, and then we'll clean this up. I will go into here and I will grab the what was it compare.
Let's go back to here. Compare at price V2, put that in here, and close
it just like that one. Now we're going to get the
two prices side by side. Maybe not. We have
got an error here, which is interesting,
that does seem to match up to what we're returning here. Let's just use a console
log to debug here. I'm going to console log
products object here. We can see what's
being returned. You've got title,
handle featured image. Let's go into variants, nodes, and rerun. Scrolling up here if we go. I see. Sometimes compare out price V2 is null and that's
why there's an error. Sometimes there is a
compare at price V2, but sometimes it's null. What the money component
is trying to do is access and attribute on
an object that's now. We need to make sure
that we're checking whether this is actually null. We can do here is if it is
returning a truthy value, then we can put in this basically return statement here and then run
that compare price. Here you can see on
these ones where there isn't a compare at price
is just showing one price. Where there is a compare at price it's showing both prices. Now, this is messy obviously. Look at this, this
is not very nice. What we're going to do is
clean this up here and then put in something a
bit nicer down here. I'll get rid of
this console log. What I'll do is I'll use
object de-structuring to take off these attributes
off of this node right here. What I'm going to do
is grab the price V2, and I'm going to pull
it off as just price. Then I'm going to grab the compare at price
and I'm just going to pull that off as
compare at price, not the compare at price V2. We don't really need that V2. I think that's just API
versioning right there. Then we're going to grab that
off what we have down here, product.variants.nodes, and then grabbing the
first one in that array. But we need some fallback in
case any of these are null. We're going to put a
question mark here. If it is null or undefined, we'll just pass through
an empty object. Then instead of checking whether this is
now what we'll do, which is a bit more
sophisticated a bit nicer, we'll just check
if the compare at price amount is greater
than the price of it. I'll call it is discounted. Then we'll put in
a Boolean here, compare at price question
mark in case it returns null, amount is greater than price question mark in case
it returns null amount. Then, so here, I'm
going to change these right here
too, is discounted. If it is discounted, it's going to return this. Instead of having the price
represented really long here, we can just use the price
that we pulled off up here, price, and then
compare at price. If I hit "Save" on that and
let's refresh over here, you'll see we get
the same result, but it's a bit cleaner
and in our JSX, it's a lot nicer to read. I'm also going to give
this one a class name of products compare at price. Then if I hit "Save" and reload, now you can see that
compare at price is clearly the markdown price
visually because we've got some CSS that
applies on it now. One other thing I'll
show you before we wrap up is, as you can see, some of these have.00, which you may not want
showing on the front end. Obviously, if there's a value
in these decimal places, you probably don't
want to round up. But when there's
something like 600, we don't really need to
go to two decimal places. What we can use is similar in Shopify liquid with
price formatting, we can put in something like
without trailing zeros. Then if I hit "Save"
on that and go back, you can see all
the products that had.00 are going to
have that removed. That's another cool little
formatting option there. I'm going to leave it there
for the catalog page. What we'll do in future is
we'll make these linkable. But because we don't have a
product route created yet, is just going to result
in heading to a 404 page. We'll keep it like this for now. In the next video, we're
going to learn how to send through a collection
handle in the URL and query our storefront API to return only the products
within that collection. Essentially, we're
going to be creating a collection page
in the next video.
9. Building a Collection page: So in the last video, we created a catalog route, which is showing all the
products in our store. It's not filtering by a
particular collection. Actually correction,
it's only showing the first nine because we have only queried
the first nine. But if we extend this
to 100 or whatever, we'll get the first 100 or up to however many that are in
our store to begin with. If we put in 100 here and I
hit "Save" and rerun this, we won't get 100 because
there's not 100 in total, but we'll get up to 100. So I believe there's 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12 snowboards within
this hydrogen preview. I'm going to put
that back to nine. Notice make our site run faster, but we can always update that
to however many we need. In this video, what
we're going to do is create something
very similar to this, so we'll create
another product grid, but with this particular route, it'll be a dynamic route. So we'll be able to go to collections/
something like shoes, as I've got pre-filled here, and then we can go and look at a product grid that's filtering
only to that collection. What we'll do for this
is we'll head back into our code editor here and
we'll go into routes here, and because it's going
to be a Nested Route, so it's going to be collections
and then it's going to have the name
of the collection or the handle of the collection. We need to create a folder
inside our routes folder here, call it collections, and then in here we're going to do something which is
a little interesting. We're going to put
in a dynamic route, we're going to put
in the handle as a variable and then
type server.jsx. Now what I'm going
to do here is I'm going to pull all
this information, basically copy and paste
everything from this right here into this right
here, and then of course, I'm going to need to update this from catalog to collection, and that's all I'll do for now. Let's see, we've
got an issue here. We need to update the
puffs up here that lead to our components because
we are now nested in, as you can see here, another folder within our route
so we're going to need to add dot, dot dash, another set of those two, both of these so that
we can navigate to the right place or run
npm, run dev again. Then if we refresh over here, we should be all good. That was one thing
we needed to update as well before it can run. Really the only difference
here in this route compared to our catalog
route is that we're going to pass through
the handle and so obviously we're going to
need to use that information. We're going to need to
pass that through to our query down here and then the query is going to return only the products that
are from that collection. But essentially here in our JSX, there is virtually
nothing to update. What we're going
to do in order to use the route parameters, is input a new hook
from Shopify hydrogen, might put it here. useRouteParams, is the
name of the component and then we can use
this hook to pull off the handle from
the route params, so I'm going to use
the structuring here, grab the handle, which will
come from useRouteParams. Now in order to
pass that variable, that handle through to our query using the
useShopQuery hook, I'm going to have to add
in my variables here. I'm going to open up
an object and then I'm just going to pass
through the handle. Now if I scroll down, I can use that handle
inside our query. How do we do that? First of all, I'm going to rename this query to something more appropriate. I'm going to call it
CollectionDetails and then in our title, we want to specify that we are going to pass through a
string as the handle. Then instead of
looking for products, I'm going to use Command
X to cut to that, so removing it from our
query to start with, but then I'm going to
paste it back in later. Now what we have
to do is actually query finding a collection, and the way we're
going to do that is by its handle, just like this. Then inside here, we're going to grab some details
about this collection, ld, title, description. Let's grab some SEO
information as well. That's a selection, so
within that selection, we need to grab the
description and the title. We could grab the image
of the collection. We're not going to use it
in this particular class, that's just an example. But we can pull
whatever information that we need off that
collection object, and then I'm going to paste in that code that we had before. We can then on that
particular collection. Find the first nine products and then use that
same information. Let's just try and fix
the indenting here a little bit, that was futile. If I hit "Save" on
that, let's now run. Actually we need to know what is a collection in our store
so in order to do that, I'm just going to go
in here and query our store right here for
the first nine collections, and then I'm going to type in
nodes to access each node, and then I'm going to
look for the handle. This is just in order to see what handles are available on the store at
this present time. So here's all the different collections
via their handles. The first nine at least, but there's only 1, 2, 3, 4, 5, 6. This is all the collections
within this particular store. I can now go over here, go to collections,
and then put in that dynamic route passing in the handle of the collection, I'll hit enter on that. We've got an error,
so what have we got? String isn't a
defined input type, so let's go back
to our query over here and I have mistyped this. I need to put this as string
with an exclamation mark. This is just a syntax error on my part and then if
I refresh over here, what have we got now? Undefined reading nodes. This is related to the
setup I've got up here, because now our nodes are
nested within products, within collections,
not just products. Let's just comment this
out for a second and I'll console log the data that's
being returned from the API, so if I scroll up here, you'll see we've got
collection first. So we're going to
need to update that. Let's cut this like we did
before with the query, put in collection and
then paste after that. We need to put in
another curly braces and then products nodes. Now you can see it works. This obviously looks quite
similar to the catalog page, so if we want to confirm that this is actually
that collection, what I can do is let's
put in a h1 here and we can grab data collection dot. What did we access? Title? I've accidentally opened up two curly
brackets again here. We just need one on either side, and then if I hit
"Save" on that, refresh over here, got
another error here. I haven't set this up
properly, so data collection, I think we need to
go data twice here, data.data.collection.title, and here you can see
Freestyle Collection. Then if I go into what was
one of the other ones? Back country, so if I
go into backcountry, now we can definitely see that the list of products
showing up different, this time, definitely compared to the
freestyle collection. Now you can see we can
pass in the collection handle and return
only that collection. Now this is ugly, so let's clean that
up a little bit. Let's do data collection, collection from data, and then should be able to just pull the collection
title off like that, and we can probably
make that even nicer by putting that up here. Then this structuring from
the collection, whoops. Now we just need to
whoops, collection. Let's see if that works. So that's a little
bit nicer as well. I went through that
a little bit fast, but basically I'm just
structuring enough from the data object to get the collection and then see me, I've got that
collection object now. I'm then pulling off the
product nodes from that now. That results in less
nested destructuring and allows us to grab stuff
like collection.title, and nodes off of these
objects right here. So because we've already created our product grid item
and recycled that, there's not a lot
of work here to do. What we've done in
this video compared to the last one is pass
through that handle so obviously this one didn't have any
handle coming through and we just accessed
products on our query root. Now we're accessing the
products on our collection, and of course we need to specify which collection we're after. We do that via the handle and we figure out the handle
via the route params, and it just means that
we've got one level of nesting deeper because we're looking inside a collection, so that forced us to update our object
destructuring up here. But otherwise we have now got collection pages set
up on our store. So in the next video,
what we're going to do is create our product page now, so we can actually, when we click on these items, go into each of these products.
10. Building a Product page: In the last two videos, we set up our product grid
inside this catalog page, which returns all of the
products within our store. Then our individual
collection route, which is a dynamic
route which we can pass through the handle of any
collection we want to load. I apologize for the slow
connection speed here. But here you go. Here's
our freestyle collection. But as you can see when
we click on any of these, they are not linking to any
individual product page, because we haven't
built that yet. That's what we'll
do in this video. In the video after, we'll create the
cart functionality. In the next two
videos, we might see the complexity
increase a little bit because the two biggest
parts of interactivity, you might say, within
a Shopify website are often variant selection and
the cart functionality. These are the areas of our Shopify site
that we usually want to update instantly managing
state on the front end. Luckily for us we're using React and we're using
Shopify Hydrogen. All of that asynchronous
functionality is going to come naturally
to this platform. It's one of the benefits of
using React and Hydrogen. But it's going to mean
that we're going to use a few more providers and a few more hooks in order to get that functionality going. But as you'll see, after we run through this, you'll start to see that there
are so many components and hooks in Hydrogen that are
enabling us to do this. It'll be quite simple once
you get your head around it. Before we actually build
this product page, I'm going to update our
links up here because we do have a catalog page and
a collection page now. I'm going to go over to
our layout component, and then in the first link, I'm going to link
that to our Catalog. The second link,
I'm going to link that to collections Freestyle. Might as well just do
this one, Freestyle. If I hit "Save" on that and
it will refresh over here, you'll now see that
we can navigate to our Catalog through
our header navigation, and we can navigate directly to that Freestyle
Collection through our header navigation as well. Let's create this
products route. Just like we did
for collections, we're going to create
a dynamic route, but we've got to nest it
inside our products folder. Then in here, I'm going to do the same title as before,
handle.server.jsx. Then as I usually do, let's export a
component right away. I'm just going to
call this Product and then a return statement
in here for our jsx. It doesn't like me doing that. You always got to put
some jsx in-between here, otherwise it freaks out Let's
just put in some jsx here. That way it won't
stop our server from running npm run dev. Because before we start to
build out this component, what I want to do is
start testing out some GraphQL queries right here. Instead of query products, let's do a query product. Then in order to target
a specific product, we're going to do
the same thing as we did before with collections. I'm going to specify that
we are going to pass in a handle and the type is
going to be a string. Let's knock out this stuff here. Then what I'm going to do is grab a product via its handle. The handle is going to
be passed through in the variables of our
useShopQuery hook, just like we did for collection. Now once we determine
the product, I am going to return the title of the product.
What else do we need? Description, HTML, and then we'll want to return
the image of the product. We can go through the media. Media can be 3D models, it can be images,
it can be videos. But to keep it really
simple for this class, we're just going
to handle images. I'm going to open up media here. Also to keep it simple, we're just going to
display one product image. I'm only going to select
the first bit of media. In this particular
Shopify store, we can attest that
the first media is going to be a product image. Otherwise, maybe
we would want to look for multiple media and then make sure that we are finding an image out
of this list of media. But I think it's pretty
safe to assume that the first bit of media
is going to be an image. Continuing with that assumption, I'm going to open up nodes here. Then for the media fields, again, this is assuming
that it's an image. If you wanted to make this
work with all types of media, you would need to
do what I showed you in one of the
theory lessons, which was having
those three dots and saying on MediaImage. Actually let's still do that. If it is a MediaImage, it's going to return
this selection, ID, and then the image itself with the URL,
width and height. Again, we're only going
to be dealing with media images in this
particular class. But if we wanted to have a different selection
based on if it's video, we can just go on Video and then put our
selection for videos. You do on sources. This is just like the
example that we saw earlier on the Shopify
documentation. But for this class
to keep it simple, I'm just going to assume that we're only
looking at images, which I believe in this
particular hydrogen storefront. I don't know if there's any
3D models or videos anyway. It's a pretty safe
assumption to make here. Then let's just run
that to start with. Actually, we need to pass
through a variable here, so it's probably
not going to work. We don't have a variable. Maybe I want to play
around with it here because we don't actually
have a handle the work with, but let's actually grab this, and let's put this
into a query here. If it breaks, it'll break within our application and
we'll just test it here. We'll do gql to house our query. Then we'll put it there. Then of course we need to import, useShopQuery that all
important hook from Hydrogen. We also need gql. Then if we're going
to use CacheLong, grab CacheLong as well. Then in here, let's
just do a const data. We won't destructure
it just yet. Then we'll useShopQuery. We will do the query as the constant query that
we've listed down here. Cache will be CacheLong,
just like before. Then we'll pass in the
variables of handle. How we're going to
pass in the handle, we're going to have to
use route params again. I'll grab useRouteParams. I'm running through this
pretty quickly because this is the same pattern we did for
the collection as well. We're just going to
pull off the const handle from the
useRouteParams hook. Then if we don't
specify handle here, we can write the short
form of just handle. Obviously we got a bit of
an error here which has caused our server to stop. But let's just console log
this data and see if we've got an issue with our
code at the moment. That looks okay. Now
we're still going to need to pass through a handle, so let's build that into
our ProductsGridItem. I'm going to input another component up
here from hydrogen, the link component, and I'm going to
transform this from a div into a Shopify link component. Then in here, I'm
going to do to, grab the product, and go to its URL. Actually, before I do that, I'm going to need to put in the preceding products
part of that path. Let's hit save on that. If we go back over to here, and let's go to that
freestyle collection or any collection page or catalog page where
ProductGridItem is in use. Now our images are
currently linked. If we inspect any of these, you can see that it's
heading to undefined. That's not going to work
[LAUGHTER] undefined. We got to figure out
what the issue is there. Product URL. Perhaps we didn't request that. Let's go to catalog. We're not going to use the URL, we're going to use the handle. That's how we're going
to construct the URL. Refresh over here and
then have a look here. You can see snowboard is
the handle on that one. The handle for this one is mail-it-in-freestyle-snowboard. Interesting names
here. If we go here, we can go to that
particular route. Currently, there's nothing
coming up on the front end. Let's go back to
our code over here, and of course, we
console log to the data. Here you can see, and I'm going to expand this
out so you can see easier, we see what is returned
from the Storefront API. It looks like our
query has worked, we have passed in the handle, and we have gotten back the title and the
description HTML, and an object
containing the media. We'll have to go deeper
into that object if we want to see what's inside. Now I'm just going to add also the variant information
to this query because we're going to definitely need
to use that in order to do the variant selection
once we get to it. I'm going to attempt
to grab all variants. I think the total number
of variants you can do in a Shopify store is 100 anyway. I'm going to try and
grab all the variance using the argument of first 100. Then that's obviously going
to return a list of nodes. Inside of nodes,
I'm going to grab the variant ID and a bunch of other properties
that are going to help us. We're going to need
to grab the priceV2. Then of course that
is a selection, so we're going to need to grab the amount and the currencyCode. Let's also grab the
CompareAtPriceV2. Same thing again,
amount, currencyCode. If we wanted to, we can
grab the variant image, but we're just going to use
the one image that exists on the product for this
particular class. But one thing we
need to bring in which is going to
be essential for figuring out which
variant is being selected is selectedOptions, so we can grab the
name and value. I think that's all for now. If we reload over here, as you can see, it
just says object. Let's have a look inside
of data variance. All we do, we need to nest. Now we need to go into
data product.variants. Let's refresh the
page over here. We've got an error
undefined on variants here. Let's have a look. Data, product, variance. Product is coming back
undefined, which is interesting. Go back to console log-in data. I'm leaving this
in the class for you guys to see how to debug. If you don't know what's
coming through in the data, you can always just
console log it and then you can
figure out how to do your destructuring and accessing different parts of the
data that gets returned. As you can see here,
we've got a data object. I think I need to
type in data again. Data, data, product.variants. Here you can see that's what
I needed to do. Here we go. If we go down here,
we can see the list of variant nodes and you can see the Storefront
ID of that variant, the price as an object, and the selected
options as an array. If we wanted to go
even deeper into that, go to the first variant, so we're going into nodes, the first node that comes
through on that variant array, and then go into selectedOptions just to have a look inside. As you can see here, for this particular variant, the size of 154 cm, and the color of syntax. This is what we're going
to need to know in order to determine the variant. Basically what we need
to do here guys is pull off the product off of
this data right here. I can either destructure
down here like I did before and do
this off of the data. Or I can save some space
and move this up here, which makes it look a little
bit more complicated, but it's a bit cleaner. That's how you'll see it in
the Shopify examples as well. We should now have access to that product if we're passing
through a correct handle. Then what I can do
here is product.title. Then if we refresh over here, still not coming through. What are we doing wrong here? Nothing is coming through in this div, which is interesting. Let's have a look at
our product objects. If I refresh, you can
see product title here. I believe that it might
actually be nested. Product.product.title. Yes, and that works. That's a bit funky, isn't it? Let's do data. Oops. We'll clean
this up a little bit. Data, data product. Then now we should be able to remove one of those products. Nope, doesn't like that either. I'll do products, and there we have it. We needed to update
our destructuring here in order to get
that product object. But now we have that
product object, we can start to
use it right here. Now one thing you'll notice
is we've lost our layout, so let's update that. We want to put in
our layout component here and nest this inside. Let's also give this product
page some SEO information. I'm going to grab
the SEO component. Inside of layout because we
are querying the database or querying the
Storefront API in order to get the SEO information, I'm going to wrap
it in a suspense. We need to import
suspense from React. Then inside here,
the SEO component. For this, we just need
to type in type product and then just pass in the
product as the data object. The SEO component will do
the rest of the work for us. Then down here, I'm going to
give this a class name of product-page and
container which lines up with our predetermined CSS. Another area here,
layout is not defined. Cool. Actually, let's put it down here because
it's our own component. Import layout and it's already pre-filled the
rest of it for us. Beautiful. There you go. We've got our header
layout back now, and so if I go to any
of the products in any of the collections
and click on them, you'll see it'll
go to that handle, and then it'll load that
product information. Right now we're only
using the title. Let's actually build out this product page
a little bit more. To do that, in order to decouple this querying of
the Storefront API inside the server component, I'm going to create a client component for
holding our product page. This is probably going to
be a bit of a longer video because there's a lot of
code to get through here. But let's just clean this
up a little bit here. Definitely going to want
to have a look at this, but then we're also
going to want to create our productDetails
component here, Client.jsx. Then again, like I do when
I start every component, export default function,
name of the component, we'll call it product details. We're of course going to pass in the product data as
one of its prompts. Then of course we're going
to return some form of jsx. Make sure not to save without anything in here otherwise,
it's going to error. On that note, let's just create a div to avoid that situation. Just like we did before, I'll put the product
title there. Then instead of this, what I'll do is I'll import productDetails
from that address. Then I will replace this with productDetails and then passing
in the product as a prop. There we go. If I hit save
on that, refresh over here. Another error. We've got this no localization
context available error here which is a
tricky one to debug. What I found was I just had to use Yarn to clear the cache. This is what I was mentioning
earlier about Yarn. I was initially using
npm run dev every time, but then I installed
Yarn because it ended up being the only way to
get around this issue and so instead we're going to close down the server
which I just did and then I'm going to run
yarn dev --force. That's going to
re-run our server, and as you can see
now it's working. That's just a weird quirk within Shopify
Hydrogen development. If you ever have a weird error like that again
and you can't figure out what's going on give this
command ago, yarn dev --force. Obviously in this case it has
worked and we can move on. What I was trying to show
you before that error came up is that we've
now moved that dev with the product title
into its own component and then replaced that with the reference
to the components. Now if we have a look
at it in our browser, it looks exactly the same. But the reason why we want
to move this over into its own component is related to server-side and
client-side rendering. As it says in the documentation
if I look over here to the React Server
Cmponents overview in the documentation
and I scroll down, you can see here
that we should use server components
when making calls to the storefront API and
we should generally use client components when we're doing client-side
stateful interactivity. That's why we are now going to break off the
product details. We should anyway, just to make it cleaner code, but this perhaps
explains more why we're using a client-side
component up here. We're seeing our first
client side component in the class so far.
Let's go over here. In order to expand out this
Product page I'm going to import a lot of stuff
from Shopify Hydrogen, so let's get started right away. I'm going to import some providers and hooks
here that is going to allow us to do variant
selection much more easily. I'm just going to put in the path to hydrogen here first, and then I'm going to break up the curly brackets like
this because there's going to be a bit of a list here that we're going to import. First of all you want the
ProductOptionsProvider, then within that
we're going to use the useProductOptions hook and then we're going to render
an image obviously; ProductPrice which
is very similar to the money component but is related specifically
to ProductPrice. Then the Add to Cart
button which we might not use in this lesson, but probably in the next
lesson when we start to incorporate the
cart functionality. Now instead of
what we have here, I'm going to wrap the
entire product details in the ProductOptionsProvider. This provider is
going to give us some key functionality
regarding variants switching, and because this can affect
everything including the product image
theoretically I'm going to put that as the
top-level component. In order to get this to work, we just need to pass in the
product as the data prop. In here I'm going to
create an image and we just need to pass in the data exactly where
that image lives. Let's console.log up here
so we can figure out how to navigate to where that
image information lives. I'm going to console.log the product object that gets passed through
and to ProductDetails. right here I'm going to remove that otherwise there's
probably going to be an error. Now let's switch over
to our client-side in order to read
our console.log. Because this is a
client-side component, we now see the console
logs in our browser on the client-side rather than here in our terminal
on the service-side, so that's something to note. If we're working with
server-side components, any console logs
will happen here. If we're on client-side
components, any console logs will happen in our client side,
in our browser. We've got an error here because we're not putting
anything in image, but essentially we've still got the console log coming through. If we go into media nodes and then go into
this image here, this object should be all that's required to get the image
to work. Let's have a look. We're going product media nodes; the first node, and then image. Let's do that,
data={products.media nodes[0]; we'll grab the first one of
the nodes, and then.image}. I'll hit "Save" on that refresh over here and let's
see what happens. The data prop should have
the alt texts property. It's not liking that,
let's have a look. Maybe we just need to pass
in the alt as it says here, so let's go down to our
image and do Alt Text here. Let's have a look inside
our elements main image. It is rendering, but
it's freaking giant. It looked like it
wasn't showing up, but it is actually showing up. I just have to put in a class name here to
get it to behave, so I'm going to do className. Again referencing
the CSS I've already created for you it's going
to be product-page-image, and then now you can see it's
nicely on the side there. Again, this is
assuming that we are only using images for
our product media. This would essentially
break down if somebody was using video or 3D models
in their product media. Just keep that in mind, this is a simplified
version for the purpose of this basic class
on Shopify Hydrogen. Let's now inside of this ProductOptionsProvide
component put in a second component, and this is where I'm going
to put in my product form. Let's create it first. This is the first time
we're going to see two components in the one file. We don't need to export
it because we're using it directly in this file, so I'm going to call
this one product form. Let's do a return
with a div in here so that the whole application does not break just
as a placeholder. Then what I want to do is
actually I want to make sure I'm passing through
a product as a prop, and then I can grab that ProductForm
components which we've just defined down there. Let's make it
self-closing like this, and then I can pass through that product as
the product prop. Then just like before, I'll just put
products.title here. Let's run that, see if it works. Now you have the products
image and the title here. Now nested inside this
ProductForm component is going to be the
useProductOptions hook. Inside of here is where
we're going to start to create our variant
selection functionality. Right here; and this is probably the most complicated part of the video so bear with me guys, I'm going to pull off some
things from this hook; useProductOptions, and this useProductOptions
is the hook for managing the state of what options are selected and therefore
which variant is selected from those options. What we can do is
pull off the options, we can pull off the
selectedVariant, we can pull off the
selectedOptions, and we can pull off the
setSelectedOption method. It's easier for
you guys to read, let's nest it like this. We're grabbing all of this from the useProductOptions hook, and then in here
what I'm going to do is start with a div but I'm going to put the title as
a h1 and then underneath that I'm going to utilize
that ProductPrice component, so I'm going to grab
the ProductPrice, give it a class name
of product-page-price. For CSS I'm going
to pass through without trailing zeros
as we did before. Let's break this down onto
multiple lines for you guys. Without trailing
zeros, I'm going to pass in the product object as the data prop and then
I'm also going to pass in the variant ID. This is important because the ProductPrice
could change based on the selected variant
ID and we can actually grab the selected
variant ID like this. This is really cool. We're grabbing this which is a variable on this state object, so this is actually
going to update as it updates based
on our selection. That is the power
of using this hook, and that is the state
that we were talking about in this
particular component that we're going to use. You'll start to see this
working in just a second. If I refresh over here, you can see we've got a
price coming up here. It doesn't actually
have trailing zeros, so that without trailing
zeros isn't working on this particular page but for any page that has two zeros; two trailing zeros,
it will remove those but as you can see here the price
does come through. Again, my shoddy
Internet connection is making this load slowly. Internet situation sorted
for the time being, sorry about that. There you go. We've got our price coming
through right there. A good idea would be obviously to create another ProductPrice
here and do what we did on ProductGriditem
in terms of if it is discounted show the
Compare at price as well. But I've already shown
you how to do that, so you should be able to
figure that out yourself. In the interest of
time and making sure this lesson does
not get too long, let's get on with the most important part which
is the ProductForm itself. What you're going to see
here is that I'm going to nest another
return statement, so this is going to be
based on an array and this section we're going to house in a div with the class
of ProductOptions. Then in here we're
going to access all the options in order to
create our product form. We're going to use map again, we're going to take
each of the options. We're going to use the
structuring to grab the name and values
off of each option. Then we're going to
create a full back here, just going to have to
add a bracket there. So this doesn't break
and remove that. There needs to just move this
here to address that error. There we go. The red
squiggly lines are gone. Then what we're
going to do is put an if statement here just in
case there's only one value, in which case there's no
option for selection. If there is one, we're just going to return null. This is just a
fallback in case there aren't any multiple values, in which case there's
no point selecting from options if there's
only one option. Then if that's not the case, we're then going to return
another set of JSX. I'll try and do this
step-by-step as possible. So we're going to first
create a product option group for each group of
product option group. I'm going to put in a key here because it's a
requirement of react. We can use the name here as the key and then
I'm going to put in a elegance elements here with the class of products
option dash name, and then I can put in
the name of the option. Let's leave it there for
now and let's have a look. As you can see, what
we're going to get back is the list of
different options. If I go to my catalog
here and go to, I believe it's this one with
the most amount of options. You can see when now listing
all the different options. But what we're not listing
yet is the values. Let's go through that right now. In this next bit here, we're going to nest
another return statement. This is where I'm saying it's starting to look a
little funky here. There's a lot of
nesting going on here. It's just the nature of
the beast unfortunately. We're going to use
another map here to pass our values into some
JSX to display. I'm going to grab the
value, open this up, and then I'll put some code
before the return statement. But just to start us off, just to get something
on the page, I will open up my
returns straight away. Here is where I will put in a div with a class of
products option value, and inside here is where I
will put a radio button. We'll do type of radio. Name is going to be equal
to the name of the option. Then of course we want
to record the value. We are currently looping through all the values so we can
pass in the value that we're currently looking
at and then we're going to pass through
and onChange method. This is when we start to use the sets lectured option method that we grabbed off the
use product options hook. Now I'm going to
put in an anonymous function set selected option, parsing, name and value and
close off that input element. Now let's see if
we get any errors. It's not completely complete, but I wanted to show you
what we have so far. We have some space here, but there's nothing
coming through. Let's have a look.
We've got the legend and then we've got an
input for each of them. Oh, I forgot to put
the label here. The reason why these
inputs I've nested within these divs is so we
can put a label in, which is a nicer look for the input rather than having
those ugly radio buttons. It's just an HTML thing here. I'm going to create this label
and then I'm going to put the value here so
we can actually see what value we're selecting. As you can see, the values
are coming across now. As I hover over each option, there is a line that
comes underneath. As I'm clicking these right now, the set selection option
should be working. We're just not getting
visual feedback on our form. In order to get that, what we're going to do is create a variable up here called checked and we're going to access the object that
we haven't accessed yet. We're going to access
this which we haven't used yet, selected options. If, when we pass in the
name into selected Options, it equals the value that
we're currently looking at, then checked will be true. Now we just need to pass
it into our input here. Checked equals the
value of that one. Hit "Save" on that.
Now you can see actually it's still not
working because we've got one more thing to do here, and that's created our id. We're going to do const ID. Let's create a
little string here with both the name
and the value. Then we can put that both here and then also in the label, put HTML for ID, and that'll link our
label to our input. This should be all
that's required to work. Now if I click on any
one of these, okay. It's not working still. What have we got going on here? Each child in the list should
have a unique key prop. Maybe that's the
problem we have here. Right here, let's use that newly created id as our key prop here. Hit "Save" on that.
Refresh over here. We're still not able to
switch our options here. We've got an error
here related to what do we got encountered two children with the same key. Let's have a look. I forgot to put in my curly
brackets here. It's literally putting
in dollar sign value. It's not a dynamic value. As you can see here,
you can already see now that it is saving a
different selection. Hopefully that wasn't too
hard to follow along with. As you can see here
as I switch variance, the image is not changing, but the price is so the use
product options hook here, is actually managing
our state for us, which is really cool. Anytime we're creating
a new selection here, we're selecting a new variant
and that is automatically updating our price and anything else that is related to
variance on our page. The one thing we're
missing here, just to finish it off
before we move on to the cart page is the
description here. What I'm going to do is create a div with the class of
product description. Instead of inserting
HTML in there, what I'm going to do is type in this very weird-looking
prop here called dangerously set in a HTML and
then in here to underscore, underscore HTML and then put in our description
HTML for the product. Description HTML. Let's close this so
we can have a look. Then as you can see,
here is our description. Now, why have we put it in
this prop and not just, let's just delete all of that
and then throw it in here. Well, let's have a look at
what happens when we do that. I'll hit "Save" and
as you can see, it puts through
the literal HTML. We need to actually insert that HTML through a prop
for rendering. The reason why the prop
is called dangerously set in HTML rather
than just in a HTML, is it's a reminder
from react to be very careful when inserting HTML into an element that is coming from a database or
an external source. The reason why is something
called HTML injection. Somebody could actually
break your page by putting in
incorrect HTML here. So it's a warning
from react to let you know that this is a
little bit dangerous. But in our situation
when we're inserting product description
HTML, as long as we, the users or the
admins don't put in any broken HTML into our product description
field, this should work. But if the client or
the admin or whoever is adding descriptions to products
puts in some broken HTML, then this is going to break. All this does have the
potential to break. That's why it says
dangerously set in HTML. That's something to note there. That is a bit of a risk. This one has been a big one. I hope you have followed along. If you get stuck at any point, obviously leave a comment. But now if we go to any
product in a store, some of these are
less complicated. This one only has one option. We can select just a size. But this one right here,
the hydrogens snowboard, I believe is the most complex. It has size, binding
mount, and material. We'll see in the next video when we add the "Add
to Cart" button, that when we click that
"Add to Cart" button, the variant is already
determined from our selection, and so that variant will
get added to the cart. So thanks for following
along in this video. In the next video,
we're going to do the biggest part of our app, which is the cart functionality. Basically the store
doesn't function until we have a
cart functionality so this will be the final part
of making our store work. Maybe take a break,
get yourself ready, and come back for the
final lesson before we deploy our app to
our Shopify store.
11. Cart Functionality pt. 1: All right guys, it's time for the final and most
essential part of getting this store to work. This simple store,
obviously there's a whole bunch of improvements
you can make here, but in order to get a
basic functioning store, there's one more thing
that we need to do and that is to enable
cart functionality. In this video we're
going to stretch across a lot of different
components here. We're going to update
our app component, update our layout
components to put in a cart button here, update our product page here
to put in an add to cart button and create a cart page. Without further ado,
let's get started. We might have to break
this up into two videos depending on how long this gets. But the first thing,
as I mentioned, I'm going to open up our
app.server.js file here. In order to get cart
functionality in our app, we need to grab the cart provider component and then we want to wrap
our router in that. I'm going to cart that. It's still in the clipboard, put in our cart provider there, and then paste back in that router in-between
the cart provider tags. I'll hit "Save" on that. Now, just like that, we're going to be able
to use the hooks and functions of the
cart within our app. Let's go into our file
explorer here and let's create a new route
for the cart page. Here now routes folder,
cart.server.jsx. In the official
Shopify tutorial, they use a drawer for this, but it's bringing in
external libraries and pasting in a lot of code. I definitely recommend having a drawer as a design feature, user experience wise, but to keep it simple and focus on what we need to
actually get this working, we're just going to
do a separate page. Just like product details, we're going to open
up a route here and then serve through a
client-side components. This file will be pretty simple. We're just going to export
default function cart, and then in here return, and then we're going to wrap everything in a
layout component. I'm going to open up a div
with a class of container, so everything is contained, and then I'm going to bring in a client-side component that I'm about to build code cart page. That's basically it. All we
need now is our imports. We're going to import layout from components/layout server. We've got a layout there, and then we're going to have
to create this components, so I'm going to create a component called
CartPage.client.jsx. Then we can create a
component in here, export defaults
function carts page, return, a basic
div to start with, and then we can import that. Import cart page and the rest is pre-filled for
us from Visual Studio code. We wrote a bunch of code
there, all basic stuff, all stuff we've done before, let's see if it actually worked. If I go to my cart path here, if we look inside
our elements here, go into main, we can see there's a container
with a div inside. We can guess that
this is working and we're on this
particular route. That's all we need to
do for our cart route. All the work we're
going to be doing for the cart page is going to be in this client side component. Just like the product
details component, we're going to have
to import a lot of different components and
hooks from Shopify hydrogens. I'm going to do
what we did before, break it up over multiple lines, and put the from ready to go. I'm just going to go
through all the hooks and components that we're
going to be bringing in, so we're going to need to
use the use cart hook. We're going to use a
provider component called cart line provider, we're going to bring in the image component for
showing an image, we're going to bring in
the use cart line hook, we're going to bring in the money component
for formatting money, we're going to bring in
the link component for linking to the product, we're going to bring in
the cart costs component. As I said guys,
there's a lot here. We're going to bring in the cart line quantity component, and finally we're
going to bring in the cart line quantity
adjust button component. I wasn't lying when
I said there are a huge range of components
within Shopify hydrogen, but they also have
a purpose and they make our lives easier. Trust me on that, you'll
be happy once we start to develop this page that
all of this exists. Now, what I want
to do is break up this cart page into
multiple components. What I'm going to
do is I'm going to create a suspense here. You can see it's automatically imported from react for us, and then inside here, I'm going to output
the cart table, which we haven't created yet. Let's create that now. I don't need to export
it because I'm just using it in the same file, and I'll just create this
cart table component. The reason why I
want to put this in a separate component is because we're going to be using some state
variables in here. I'm going to be
pulling off lines, checkouturl, and status
off of this use cart hook. Then obviously they
make this a component. We need to render some sort, so let's open up and
let's just throw in a table HTML element. Now, currently we
can reliably attest that there is no
items in our cart, and that's because we don't
even have a add to cart button on our product page yet. What is a good fallback
to do straight away is if lines.length
equals zero as in, meaning that there are
currently no lines in our cart, there's no items in our cart, then we can return some JSX
here and say something like no items are currently
in the cart. Now there's a certain
problem to this, and I want to show
you what that is. What I'm going to do is
put in a console log here, and let's just say no lines. If we run that, it's going to say no items
are currently in the cart. Now, we'll get back to
this in just a second. What we're going to do is, which one should we do first? Let's go into the
layout component here and populate this. I'm going to put it
in an icon here from an icon library that is
recommended when using tailwind. In the official
Shopify tutorial, they use something called
tailwind and there's an associated tailwind
icon library. We can actually still
use the icon library even if we're not
using tailwind. I forget what it's called,
but if we just type in tailwind icon library,
it should come up. Hero icons, that's
what it's called. This hero icons is a
library of SVG icons which SVG is now probably the best way of implementing icons in
any modern day project. We can do it as solid, outline, or mini. I like the outline and I'm
just going to type cart here. You can personalize
this if you want. You can use this shopping cart. I like the shopping bag, and we can either copy
the SVG or the JSX. We're using it in
a react project, so we can copy the JSX, that's fine, and then
I can paste it here. Actually, it's better that
we use the JSX because right here you can
see something I talked about in the
theory lessons. This right here is
linking to tailwinds, so I'm going to remove that. But here you can see if this
was a standard SVG in HTML, this would be stroke, line, join, or actually I
think that's one word, but there'll be a dash in there. Remember we talked about it in the theory lessons
that we can have dashes. It's actually a good idea
that we grabbed the JSX here, I'll just show you
that right now, if we copy the SVG, there's going to be dashes in attribute names
which we don't want. Definitely copy of the
JSX if you're using JSX, which is what we're
doing right now. I'm going to indent
that a little bit, and as you can see
here you can put a JavaScript expression in some of these
attributes as well, and then of course I'm going to transform this into a link. Have we imported? Yes, we've imported link here and
we're using it elsewhere. I can just go like this. Then I just need to
link to the route, which is just going to be -cart. Right here we're going
to put an indicator shortly on how many
items are in the cart, but we'll get to that
in just a second. If I head back to our
application here you can see we have this cart icon here, and if I'm on any other page, if I click on this cart icon, you'll see it brings us
back to the cart page. Let's go into a product page. Let's go into that first one that we saw once our app loads. Here we go, the
hydrogen snowboard, and let's bring in and
add to cart button. Open up our product details
component right here. We're already importing our
add to cart button up here. I'm going to go down to, let's do after the options
but before the description, and let's create an add
to cart button here. Actually we don't want
it to be self-closing because we're going
to add children here. We're going to put
in here add to cart. Then in order to give
it some styling, according to the existing CSS, I'm going to put in, add to cart as the class name. Let's go over to our
application here and you'll see the add to cart
button exists. I've just refreshed
it, but there you can see we've got our
add to cart button. Now, one thing we
want to do before we click this add
to cart button, or let the user click this add to cart button is to restrict
it if it's out of stock. We don't want to show the
add to cart button or we at least want to tell
the user that it's out-of-stock if it
is out-of-stock. Let's scroll up to
the product form here and create a Boolean here. I'm going to create a const is out-of-stock
and that's going to return true if the
selected variant is not available for sale. If that doesn't come
up, we're going to have a fallback here of false. Now, I believe that we
haven't actually retrieved this from our GraphQL query, so we're going to
have to do that. Let's go back to the
handle.server.js x inside our products
folder there. We just need to make sure
that on the variant node, we're going to check if
it's available for sale. We just need to update that, and then we've got this
Boolean flag here is going to be true if the selected variant is
not available for sale. What I'm going to do here
is a ternary operator, going to break this
up into a new line, and then put in a JavaScript expression here
we have a ternary operator. If it is out-of-stock, we're going to say out of stock, and if it isn't out-of-stock, we're going to just
say add to cart. Then also this should
work, I'll put it in. If it is out of stock, it should also be disabled, so I'll put that in
a disabled prop. That should work. I don't believe any of these
products are out of stock, so we can't really test it, but this is definitely in stock. If I click "Add to Cart" now, you'll see there's a bit
of a loading state there, but as it finishes, then that should be
now in the cart, and if I click on
this card here, now we'll get an
error because we're actually dealing with
some cart information. What does this render? Is
not defined in cart table. Let's have a look at what
we got going on here. I put render here instead of
return. I'm such an idiot. You guys are probably screaming
out from the other side of the computer telling me that I've got that wrong.
Sorry about that. If we refresh over here, it will still say there's
no items in the cart, but then it disappears. Interesting. If I refresh again, no items are
currently in the cart and after awhile it disappears. This is the problem I
was alluding to earlier. I said we're going to have to
come in here and fix this. Basically, I don't know if
this is a bug or something, but essentially the cart will return with no lines
to start with, and then it will discover
the lines later. When we first load it, it's going to say there's
no items in the cart, but then it's going
to disappear if there are items in the cart. What I've done here
is I've brought off status from the returned
object from use cart. Here let's have a look
at what status is. If I console log status and
then I refresh over here, you can see that three
statuses come up fetching, uninitialized fetching,
actually that's only two; isn't it? But there's
three that comes up. Let me just put this on the
other side so we're going to get the status no matter if
the lines are zero or not, and then refresh over here. Here you can see
we get fetching, uninitialized fetching,
and then idle. Now, the reason why this is helpful is before idol shows up, it thinks that the
line items are zero. But then when idle shows up, it realizes that's not true
and shows the alternative, which is currently
just an empty table. If we actually look
in here and inspect, you'll see that an empty
table is being rendered. But of course this
table is the table that will house
all of our items. What I'm going to do
here is in addition to checking the length
of the lines, I'm also going to check the
status and I'm only going to show no items are currently in the cart if the status is idle. Otherwise, it could be
in a loading state. If I refresh over here, if there are items
in the cart now, it's not going to show
no items in the cart. That's my little solution
to that problem. We're already pretty deep into the video and
we haven't even got a list of cart
items showing up. Let's get into that right now. I'm going to give this table
a class of cart table, again just for CSS formatting, and then I'm going to open
up a t body tag here, and then here is where
we're going to start to bring in our lines. We'll use that familiar
map function again, and we'll grab each line, and with each line, we're going to return some JSX. Here is where we're going to
put the cart line provider. We need to give it a key, so put through the
line ID as the key, and then we'll pass in the
line in the line property. Again, I can't really go into too much detail as
to why this works, because it is just the
nature of the component. These provider components provide functionality
and they're set up by the libraries
that we're using. You'll soon start to see what
functionalities provides. We're going to use another
hook inside of here, but what I'm going
to do is create another component
called cart line item. Then so let's go again,
function CartLineItem. In here, what I'm going
to do is return some JSX, obviously, I'm going
to return a table row. What I'm going to use
for that is going to be returned from another hook here. We've got another hook here of the structure some
things off of this hook, but the hook that
we're going to use here is useCartLine, and what we're going to
do is grab the line ID, something called a merchandise, and the cost off of this object. In here, that's where
we can use line ID, and then each of these
rows is going to have, I think, four
columns. I set it up. In here, in the first one, that's where we're
going to put our image. Let's just say image to start
with as a place holder, the next one we're going
to have our product title. I'll just put in fake
values to start with, so we know what's
going to go in here, variant selection and
individual price, and then the next one is going to have our
quantity selector. Then the final one is going to have our line total
and a remove button. If I refresh over here, you can see that we've got one row that is showing all
of these placeholder values. That's not particularly handy, so let's try and
make these dynamic. I'm going to remove these
placeholders, and in here, I'm going to use
the image component which I think I
already imported. Yes, I did. Very
good work, Chris. Let's go in here and give it a class name of
LineItemImage for styling, and then all I have to
do is pass in the image, which is actually in the
merchandise objects. I can do a console log here
for you guys to prove it. If you want to see what's
in the merchandise object, you can just console
log it yourself, but just trust me, when I say for the time being, to save time, the image is
inside this merchandise field. If we pass through
that and I hit "Save", now you can see that place
holder has been replaced with the product image of our only
product that's in the cart. Now we want to insert product tittle variant
selection and individual price. Let's do that right now. I'm going to put in a link
so that when we click this, it will go to the product. In here, I'm going
to do products/ and then put in the
merchandise products handle. On the merchandise objects we've got access to the product, and on that object we've got access to that products handle. Then I'm going to give
that a class name for styling of
line-item-product-title. Then in-between these link tags, I'm going to grab merchandise,
products, tittle. We're using merchandise a
lot here so we could do some restructuring here
to make our lives easier. Maybe let's do that now. Let's grab off of merchandise. We can grab, I like to
put in the best space, the image and the product here, so we can just
remove merchandise from these is the benefit
of restructuring, which we've seen multiple
times throughout this class. I've put that link
in, let's just check if that's working still. We've got the hydrogen
snowboard there, and if I click on it, it'll take me to that
product bit slow, but there you can see
it's coming through. Then we want to,
underneath this, put the variant selection. We're going to have to loop
through the selected options, which is going to
be on merchandise. I'm going to pull that
off using the structuring and then I'm going to
go selected options. May be there are no
selected options, so I'm going to do a
fallback of an empty array, and then I'm going to run map. Then for each option
renders some JSX. Sorry, this is a
mess when it comes to all these different brackets. Maybe it's just because
I haven't put through my JSX expression here, which is going to be, again, we're going to use
a key because we're looping through things, and I'm going to use the
option name as the key, and then I'm just going
to put in quite basically the option name and
the option value on this particular
variant selection. Let's hit "Save" on
that and see how that works. There you go. That is the variant selection. The formatting is
a little bit off. I haven't actually put
this in a div with the class name of line item variant in order to get the formatting
that I've already set up. There we go. Then for
the rest of this, I might do that in a
separate video for you guys because this one
is getting quite long and Skillshare likes me
to keep my videos under about 30 min
to 20 min each. We're going to do a
part two in this one, I will see you in
the next video.
12. Cart Functionality pt. 2: All right. Welcome back. I hope you are ready to get on with this cart
functionality and so obviously the biggest
feature that we'll build in our little app. So it deserved two videos in
order to cover everything. So we've got these
placeholders still here on this side
of our cart table. Just to recap what we
did in the last video, we enabled cart functionality
on the entire app by wrapping our router
in the cart provider and then we created a cart
route which links to a cart page and then we also added in a link
to the cart right here and of course we added
an add to Cart button, which is allowing us to
actually add items to our cart. Let's get back to
building our cart page. In here we're going to use some other components
which we brought in or which we imported up here, cart line quantity and cart
line quantity adjust button. That is probably one
of the bigger names of the components that
we're bringing in but these buttons are quite
helpful as you'll soon see. Now for this one, it's
important that you use the classes otherwise
it's going to look like absolute crap. So I'm going to wrap all of this in cart quantity selector and
then just to start us off, let's actually just bring in the cart line quantity and
if I just put this in, as long as this is within
a cart line provider, check this out, you'll see that the quantity is the
correct quantity. So if I was to go to this snowboard and add another
one of these to the cart. I think it needs to be
the exact same variant and it's taking a while. Now we go back to the
cart and now you can see it's up to two,
so how good is that? We're already bringing in the state of what
the quantity is on that line item just by using this component from
Shopify Hydrogen. But of course we want to be
able to update the quantity. So on either side
we're going to put in a cart line quantity adjust button and
then on the left side for the adjust parameter, we're going to make it decrease. I'm going to open that one up
and inside I'm going to put in an SVG that is
a minus symbol. So I'm going to go back
to hero icons here, find minus copy the
JSX, throw it in here. So we've got our
decrease and then on the other side of
the cart line quantity. I'm going to put in another
cart line quantity adjust button and as the adjust
prop is going to be increase and then the SVG and then I'm going to
put in here is of course going to be the
opposite, which is plus. So I'm going to copy
the JSX for that icon, paste it in here, and
then hit "Save" on that. Now let's see what happens when we refresh our page over here. You can see that I can now
reduce it down to one, or I can increase it up to three or how many that
I want to make it. So those are very
handy components there from Shopify Hydrogen. We get some quantity
adjustment functionality right out of the box. For here, for the line total, I'm going to replace
that placeholder with a money component. I'm going to put in
that without trailing zeros flag and then
I'm going to pass through the cost
dot total amount to format the total amount in a nice user-friendly
formatting. So up here is where
we're getting the cost from the
use cart line hook. You should understand by now that if we just
put that through, it wouldn't be formatted nicely. So we want to put that
in a money component, which we can see here. If we're getting three of them, the total ends up
being 1,800 and then the remove button is another cart line
quantity adjust button. I'm going to put in
as this as prop, I'm going to make it
show up as a div, going to give it the class
name of cart remove and then the adjusts prop of remove. This is going to tell the cart line quantity
adjust button component that I want it to remove all of the
quantity of this item. So just remove the line item
completely and for that, I'm going to use
a trash can icon. So I'll go into
here, type in trash, copy the JSX, head back here
just like we did before, and fix up the nesting. So if we have written all of our class names correctly
and we go back here, you'll see that it'll show up in the top-right corner here and so we can just click that and remove the line item completely. Now you'll see that when
we refresh the page, it doesn't show that
there are no line items currently in the cart until
we get to the idle state. That's to prevent this
from showing up before we actually know for sure whether there's any
items in our cart. Okay, so let's go back and
add that item in our cart. Let's do a different
selection here. Poly carbonate, classic bolt
pattern 160, add to cart. Then go back to
our cart over here and I think I wanted to put the price of each
underneath here, so let's add that in as well. We already have access to
our money component here. I'm going to type in money
without trailing zeros because that's my preference and
then inside the data, prop, put in merchandise price, v2, hit "Save" on that
refresh and you can see the individual price is 610 and if I
increase it up here, the price per each
will stay the same, but the total cost of that
line item will go up. Let me show you what
happens if I add another product to
the cart let's add a full stack snowboards to the cart and then I'll click on this to bring
me to the cart page. You'll see we've got two here. Now, we've got a
bit of a problem. One of these is showing
up bold and one of these isn't showing up
bold, which has an issue, but also we haven't got a
footer here for the total, and obviously we need
a button for checkout. So let's expand upon this. I want to go outside of
this cart line item, back it up a little bit here, and then underneath here, put in a div with the cart footer and obviously we're going
to get all these red lines because we need a single root element
in our return. So I'm just going
to do that hack that we use before of opening up an empty element and then just indenting here and then
in our cart footer, I'm going to put in a link. The link is going to go
to the checkout URL, which we pulled off of use cart. It's going to automatically
generate us the Checkout URL. I'm going to give it a class
name of Checkout button, which is set up in our CSS. Cool, so I'm going to close that and then in here
I'm going to just have the word checkout and
then close that link tag. If I refresh over here, you've got the Checkout button, but I still haven't put
in the cart totals. I think I'm going to
put that in the body. So right after here, where we're returning
all of these rows, we can put in a final row
and the first two columns, I'll do a call span of two. We're just going to have
nothing and then we will have the word total and then
for the final cell, we will bring in cart cost. Which gives us a total cart cost without trailing zeros
and we don't need to pass through any information
here as long as it's inside the provider, which I guess is the cart
provider, this will be fine. So refresh over here and you'll see that those two
added together equals that and I believe
the bolding was due to my CSS making the
final line bold. So the final line should
have been the total. So now that styling
issue is taken care of. All right. So we've got two
of this particular variant. In one of this
particular variant, if I click the
"Checkout button", let's have a look
at what happens. It's a bit slow. Sorry
because of my poor Internet. But you'll soon
see that we go to the checkout page on
the Hydrogen shop. If we show the order summary, this should match with the
selection in our cart. There you go, guys. That's how simple or not simple it is. It's a lot of code to
write but there's a lot of functionality that the
Shopify Hydrogen framework takes care of for us. We don't need to put any special code in
our Checkout button. We can just bring in
the Checkout URL. We can use components like this. All we have to do is put in CartCost and it figures
the rest out for us. These amazing
CartLineQuantityAdjust buttons, the CartQuantity itself, it's actually quite
awesome how much functionality some
of these components give us out of the box. One of the final things
I want to do here is put an indicator of how many items
are in the cart up here. What I'm going to do is in our layouts components,
where is it? Right here. I'm going to insert something
called a CartBubble. This is going to be a
component that I built myself. Then I'm going to,
underneath here, create this
CartBubble component. Actually, because we're
going to be using use cart, I'm going to move this into its own clients component file. I'm going to go into a new file, CartBubble.client.jsx;
put that in here, do an export default. Then going back to the
layout.server.jsx file, I will import that component, CartBubble and the rest
is auto filled for me. If I go back to
CartBubble.client.jsx, obviously we need to return something here; return some jsx. All we're looking for
here is the quantity. That's all this is doing. I'm going to import the useCart hook from
the Hydrogen framework. I'm going to pull off total quantity off of the
object that this hook returns. This is an easy way to
get our total quantity. If total quantity
is less than one, which is basically zero, I'm going to return null, so basically return nothing. But if it gets past that, we will return a span, and inside the span
we'll do brackets, and inside of those
brackets we'll just return the total quantity. If I save that, refresh over here or refresh
anywhere on our app. We've got a bit of an
error, invalid DOM colSpan. We need to use camelCase for the colSpan
here. Where was I? CartPage. We need to put in
a capital S here. That's a weird jsx thing. What else have we got here? If we read the error message, it seems to be coming from
our CartPage component. Let me just go to
another page on our website and confirm that it is isolated
to the CartPage. Yes, it is. If we look over here, next to this cart icon, it should indicate how many
items are in the cart. Let me go over here
to CartBubble. If there are zero
items in the cart, it should return null, so there'll be
nothing showing up. So this is suggesting that there's zero items in the cart. Let's click on the cart again, and it just returns, no items are currently
in the cart. If I refresh do I
get the same error? Yes. If I navigate through the CartPage from another
page, it'll be fine. But if I arrive directly on the CartPage,
I will get this error. I think the issue here goes back to if we go to the CartPage. This rendering without
the asynchronous request to the storefront
API returning yet. Having Suspense here
should check for that. But in this case,
it's not working. I'm going to put this
return statement. This might be a bit of a hack solution but
it should work. I'm going to paste it in here. If lines.length is
more than zero, then let's render the cart. If I refresh over here, that solves our issue. Somehow we've lost our items in the cart but that's all good. Let's go back to our
catalog or any collection. Make a product selection, and then I'll click, add that to the cart. Now, you can see our
CartBubble is working. We've got one item in our cart, and if I click over to it, you'll see that this is
our Hydrogen snowboard. If I add a second one, you'll see this number
goes up as well. If I add a different snowboard, maybe this full-stack one, click "Add to cart" you'll see that that gets
added as well. If we go in here, you can see the three snowboards that we have in our cart. There you have it, guys. If we run through
our projects so far, we've got our homepage. Obviously, we haven't
built anything here. This is for you guys to figure out what you want to
put on your homepage. You can bring in a certain collection and
feature it in all ways. We've then got our
catalog page here, which lists the first
nine products in our store regardless of
what collection they're in. If we click on any of these, it'll go to that product. Similarly, if we go to a specific collection like
the freestyle collection, and then we click on one
of these product pages, we can make a variant selection, add that to the cart. Go over to the cart here. As you can see, it's
saved that selection. I can increase the selection. I can remove it from the cart by bringing this all
the way down to zero, and then I'll say no items
currently in the cart, or if I am to add another
product to the cart again, add this 154-centimeter one
and increase the quantity, you'll see it increases
over here as well. An improvement for this
app would be to work on the loading states and build in something a little
bit nicer for a suspense. Right now we don't
have any fall backs, but you could build
in some pretty nice loading states here. We can also, obviously, checkout by clicking
this button right here, and that'll take our selection
straight to the checkout. At this point, we are
leaving our app and going straight to the checkout page as hosted on the Shopify store. Here we go. We can also remove that item by
pressing that button. As you can see here,
it takes a bit of time to build out the CartPage, but the cart functions are
pretty well handled by all of these components
and hooks that are available within the
Shopify Hydrogen library. To summarize everything
we've done in this project, I want to highlight the
themes I highlighted at the start of this
class before we even go into the
practical side of things. If we go back to what's
a simple one here, all of these have turned out
quite built out in the end. But for instance, we've got a product grid
item, for instance, is a client-side component
that we pass in a product. If we have a look at
our collection routes, we are making a request
to the storefront API, grabbing all the information
we need via a query, passing that into each of
these product card components, and using hooks along the way. If you're ever lost or confused, you can look up
any of these hooks or components in
the documentation. If you're importing from React, you want to look in the
React documentation. If you're importing
from Hydrogen, you want to look at the
Hydrogen documentation. You can literally just go over to the Hydrogen
documentation. This is the storefront API one. But I should be able
to search from here, UseRouteParams, go
into API Hydrogen, click on here, and you
can learn more about all the different hooks and components that we
used in this class. In the next video, I'm
going to show you how to deploy this app to
your Shopify store. That's going to involve
connecting to the storefront API. So I'll update our
credentials here, and then we will publish this
live on your Shopify store. Obviously, a lot of
improvements we can make here. I've left this third link here, and that's going to be
for the bonus video where we're going to add in a blog. But for now, these are
the main components you need in order to have a
working Shopify storefront. In the next video,
we're going to deploy this to our Shopify store. I will see you in that one.
13. Deploying our Custom Storefront: Welcome back guys in this lesson we're
going to learn how to deploy our Shopify hydrogen
storefront to oxygen, which is the hosting platform
provided by Shopify, which we can access directly on our Shopify store via the
hydrogen sales channel. Unfortunately, at the
time of recording, oxygen is only available
to Shopify Plus plans. But if you're watching
this well into the future, it might already be available
for other Shopify plans. This is a pretty
brand new feature, so I guess they're
just rolling it out in Shopify Plus stores as they
are the highest paying tier, we'll get priority on
this brand new feature. Unfortunately, if you
come to this page on the documentation and this
warning is still here, if you're on anything
under Shopify Plus, then unfortunately we are, this is not going
to apply to you. You have to use a different
hosting solution if you decide to deploy a
hydrogen storefront. Now, lucky for me,
Shopify have enabled me a store with a
hydrogen sales channels, so I can demonstrate
this feature. But I just want to show
you on some stores, you're not going to be
able to access this. For instance, on my
original testing shop, Chris testing shop. If I go here into sales channels and then I click all recommended
sales channels. Rounds here is where
the button to install hydrogen would live if
I was on Shopify Plus, or in the case of this store, have it enabled by Shopify. Unfortunately, on this
store, I cannot add it, but like I mentioned here
on Chris testing shop 2, which is a store I set up
specifically for hydrogen. If I click into sales channels and go into recommended
sales channels, it won't show up here anymore because it's
already installed. But before I installed it, it was available to add here. Now you can see I've got a hydrogen sales
channel right here. If you're on Shopify Plus, you should be able to
go into sales channels, recommended sales
channels, and add it here, then you will have
this menu item here. Unfortunately, if you are everyone else at the
time of recording, then you're not going
to be able to do this. I'll close this one down we
can't do any hydrogen there. Let me go into the
hydrogen sales channel. Now what we can do
is head back to the documentation
and follow along. I did this and I ran
into a few hiccups. What I've done is I've figured out what
works and I'm just going to show you
that in this video. If I head back over here to my hydrogen sales
channel right here, you can see I got a
hydrogen test here, but what we're going to do
is create a new storefront. First of all, I
want to switch back to my code editor here, and I want to make
a few changes. First of all, I want to
make a production branch, and that's the branch
that we're going to deploy to our Shopify store. I'm going to go into here, create new branch, call
this branch production. Then the next thing we're
going to want to do is change these credentials to our actual Shopify
hydrogen storefront. The usual way of
going about this is to look into
our private apps, which currently
sits in settings, apps, and sales channels. Then over here to develop apps. As you can see here, I've got storefront API
access as a custom app. I'll just demonstrate
this quickly, I'll create an app and I'll
say storefront API access. Then I'll just put Skillshare
here to differentiate it. Hit "Create app". Then I'll just need to click here to configure
storefront API scopes. I'll just check every box here. Then I can click the
green save button. After I do that, I
can click "install app", click "Install". Then here under API credentials, I'll have my storefront
API access token so I can take that and put that in here. But what's going
to happen when we deploy our hydrogen
sales channel is will automatically get a
storefront API access token. I'll show you that
right now if we go into the hydrogen sales channel,
click "Create storefront". Here is where we can create a brand-new hydrogen repository. I'm not going to do that. I'm going to connect an existing repository because we have, of course, got our project
on GitHub already. I'm going to click "Connect"
an existing repository, select "My account"
and then find that repository under
hydrogen class storefront. Now, something to note
here is that you will need to set up the
integration to GitHub. I've already done
that, therefore, this is showing up
with no warnings. But if you haven't, and I'll put a screenshot on your screen right now so you can see there is a warning box that shows up asking
for permissions. You will need to install the Shopify GitHub app onto
your GitHub account and then enable certain permissions for those guys who have been using the GitHub integration
with theme development, it's the exact same
Shopify GitHub app. It just requires a few
extra permissions. Once you've enabled the app
and given those permissions, then you'll be able to look up your repository and click this
green button for Connect. Now before we do that, I
just want to make note of the production branch has
to be master or main. It's really annoying for
me because I like to have the production branch be a separate branch
called production. But by default here on hydrogen and they don't let you
change it at present, it's going to automatically
select the master branch or I think the main branch
if you don't have a master branch as your
production branch. If you do have the
production version of your app on your
master or main branch, then this is going to
be quite easy for you. But for me liking my
production branch, on a separate branch
called production, there's going to be
a few extra steps here. Take note of that. I'm going to now hit the green Connect button and it's going to load up this new
hydrogen storefront. As you can see here, we
just got a new storefront. It's just called hydrogen
class storefront. As you can see down here, a preview branch
is being deployed. I'll just wait for
that to finish and then we'll take a look. That's finished now, and if
I click on this preview URL, you will see that
the master branch is deploying and it's pulling in my actual storefront
credentials. The actual name of this store, not the name of the
Shopify preview. Now a few things happen behind
the scenes there that I want to mention here. If we go into this right here, you can see that it added
a special file called a Shopify oxygen deployment
workflow file. It did that through the.github
slash workflows folder. This is important
because it's required in order for these
branches to deploy. If we go back here and
we look in production, there are no deployments. If I go into all
deployments here, you'll see there's
nothing under production, there is something
under preview. Then if I go into
storefront settings, you can see there is a
production branch here, but if I click on it, we're going to get a 404 page. Now, like I said,
I don't like to have my production
branch on master, so I'm going to create another
production environment. But first of all, I'm going
to update and publish this production branch to our GitHub account so we
can actually connect it. Now, like I mentioned before, this hydrogen
storefront is going to generate us a
storefront API token. We can use that rather than
creating a private app. What I'm going to do is
replace the storefront token here with the one inside of
our storefront settings. Then, of course,
the store domain is going to be
Chris-testing-shop-2.myshopify.com. Unfortunately, I can't
copy it directly, so I'll just type it in. I'll just copy that and
make sure that works. Yeah, that is the right address. Sweet, so we've updated
our permissions here, and I'll hit "Save" on that and then I will commit those
changes obviously. Stage, Update
Hydrogen config file will be the commit message
and then I'll hit "Commit". Then I'll click here to publish. Now that I've pushed that branch and it's on our GitHub account, I can go to "Add environment". I'm going to select
the production branch. There we go and I'm going
to call this production, production just to differentiate it from the other
production branch. Unfortunately, I can't
rename the other ones. This one is just going
to be production and in brackets production
because that's the name of the actual branch. I'm going to make this public and I'm going to hit "Save". Now if I go back, you can see we have a
second environment here. But if I click on the URL, we're going to get the same
problem, this 404 error. The reason why is it's
not actually deploying. If I go back here and
go to all deployments, you'll see here under preview
we have a deployment. Under production, no deployment, and under our custom production deployment
or environment, there is no deployment. Now what's the difference
between these and this one is that Shopify added this Oxygen
deployment workflow file. We're going to do that
for ourselves as well. What I'm going to do is head
to my GitHub repository. Here it is, christopherdodd/
hydrogen class storefront. Just to be clear guys, this is my repository, so I have access to
this and I'm able to create a production
branch on here for you, you'll want to make a fork or another repository and create a production branch on that. This is not the
literal GitHub project that you will be
deploying to your store. I just wanted to
make that clear. But if we look inside here
and use mine as an example, if I go here to look at
the different branches, I can see one down here, they've added a new branch, and this is our preview branch. If I look inside, you can see it's
based off master. But what they've done is
they've added a workflow file here and this is our deployment
settings right here. That's what's being served
in this preview URL here. Shopify don't make
this very clear, but that's essentially
what's happening. What we're going to do is
we're going to take this file. We just need to make
note of the path here. It's.gitthub/workflows and
then we put this file in. I'm going to click here
to get the raw file. What I'm going to do is
slightly off screen. I'm going to go
into the file menu. Click "Save Page as". Then inside our
storefront project here, I'm going to copy
the path which was to create a.gitthub folder. It's going to warn us that dots are reserved
for the system, which means that we
won't be able to see this folder in our finder, or I guess it's Windows
Explorer on Windows. But we will be able to see
it in our code editor. Then I'm going to create the work flows folder and then in here I can save
that deployment file. I'll exit out of that. Let's head back over to this and then if I go
into my code editor here, you can see that
change coming through. If I go over here
to the Explorer, you can see that we've added
that file to this path. All we have to do
is commit that. I'm going to stage that add
oxygen deployment file. I'll hit "Commit". I will push that change
to the production branch. Once that's pushed, if
we go over here back to our deployments and we go
under production production, you can see that something
is deploying right now. If I go into all, you can see that we're adding
our oxygen deployment file to our production
branch right here. I'll just wait for
that to finish deploying and now you can see that it has successfully deployed and if I click
on this URL here, long behold, here is our app. If I go into the catalog page, you can see that this is working just like
we had it locally. I can go into any one of these surfboards or
snowboards rather, add to cart, go into my
cart page and check out. That will take us to our
actual stores checkout. Regardless of whether
you want to deploy it on the master branch or not, you're still going
to need to add that oxygen deployment file in order to get that
branch to deploy. Here you can see I've created a custom environment now using the production branch
and we can access this public URL,
which is really cool. If we want that for
the master branch, we can do that as well. Otherwise, we have
this preview URL here and that shows us what exactly is required
by Shopify in order to get this
to properly deploy. As you can see, this
is not coming up under the production
heading because it assumes that the
master branch is going to go into the
production environment. But for me, I prefer to have a separate branch because
the master branch might be deploying to different
stores so I often have different production
branches for different stores. But there is of course
some common code that goes in-between stores when
I'm working with clients. That's why I like to have master branch as the
common code that goes across all production
branches and then have a separate production
branch for each store. That's a little
explanation of why I use production as my
production branch. But for some reason, Shopify assumes master as
the production branch. There you go. Now we have our production
environment and our public URL for our
Hydrogen storefront. Now that we know how to deploy
our storefront to oxygen, if we're on Shopify
Plus or any of the other plans that hopefully it will be
available in future. Now, let's move on
to the bonus lesson. We'll take a step back, build upon what we've already
created and add in a blog. I'll see you in the next video.
14. Bonus: Adding a Blog section: All right, so as I often do
in my Skillshare classes, I have thrown in a
final bonus video, which is not essential
as we saw before we have already created
collections, products and enabled
cart functionality, which is really all we need to create a basic
functioning store. But in order to gain more practice with the
workflow in Hydrogen, I am going to show you how
to create a blog setup here as well so we can pull in blog posts from
the store as well. What I've done is I created a production
branch last time, but I've gone back to
the old Hydrogen config, so we're using the blog posts from the Hydrogen preview again, this is going to be following
on from two videos ago, not the last video I just needed to make
reference to that. We are running Hydrogen preview, not connecting to my
particular store. What we want to do is number 1, create a blog layout, and then number 2 is create a route for all the
different articles within that blog post. Let me just open up GraphiQL again because I just
want to verify that we do have blogs on this
particular Shopify store, so I'll do blogs, this will list our blogs and
I'll do nodes and handles. If I run this, you must provide at
least first or last, so let's just say the
first five blogs, there shouldn't be more
than five blogs surely. There's two blogs on the
Shopify hydrogen example store, and that's news and journal. This will work, so if
we head back here, what I'm going to do is
head back into our project here and let's
create a blog route. Inside of our
routes folder here, new file blog dot server, actually sorry, I need to make that a capital blog
dot server dot JSX. Then we're going to export
defaults function blog, and then return some JSX. Don't hit "Save" yet
because it's got an error because we don't have
any JSX in there. Then let's import some of the things that we're
going to need to use, we're going to need to, of course use the
use shop query, because we're going
to be querying the storefront API to get
the blog post information. I'm going to import
CacheLong and GQL, again don't hit "Save" yet because it'll break
and then I'll import suspense from react, I'll import layout from our components layout dot server and that's what I'll do for now. What I'll do is let's just test out a query over here
in our GraphiQL, so I'm going to access
the journal blog so let's create a named query I'm going to name
this query articles, and then we can
access the blog via its handle which as we can see here we've got two
options to choose from, journal or news, let's do journal and
then within here, let's access sorry, articles, skipping ahead a bit there. We need to put in the
parameter here so let's go for the first nine, and let's type in
nodes here and then on nodes we can select the
title of each article, the handle of each article
we'll need that for navigation and then the image, so the URL and alt text. That's all we should need
if I hit "Run" on that, you'll see we get back the first 1,2,3,4 so there's
only four articles, even though we specified nine
if there's less than nine, that's what we get back. But the good news
is that all worked so let's go back to
our project over here, and I will create this query, const query equals GQL and then let's paste
the query in here, I'll just leave
the indenting like that and I was an idiot and hit "Save" without putting anything
in our return statement so we're going to
have to restart our server that's no biggie. Before we do that however, I am going to use the
use shop query hook so I'm going to grab the
data from use shop query, parsing the query like
such, use cachelong, like we've done previously, preload true and
then under here, I'm going to do const. Actually before I do that, before I do structure
let's do what we usually do and have a
look at that data object. I just need to put some form
of JSX in here otherwise, the server is not going to run, so I'll just do that
and then let's do npm run dev down here, so that's up and running now, let's refresh and let's
go to our blog route, we've got an issue here, no Shopify context found
this sounds like one of those weird errors
that we can fix using yarn dev dash dash force, refresh and as you can see
that removed all the errors. What we're looking for though, is what's in the data
object and as you can see here inside the data objects
we've got data again, we've got blog again, and then we've got articles
which holds an object, so let's destructure here, so we'll go const from the
data we're going to go inside, access the data, then
inside the data, access the blog as blog. Then what we can do is let's
just console log blog, make sure we got the right one. If we look inside here, you can see yet we've
got articles and inside articles we've got
a list of nodes cool. What we'll do is we
could go in here and put the title of the
blog but we're not going to in this
particular video, actually all we're using
this for is to grab the articles so I'm going to go one step deeper into structuring
and grab the articles, actually I don't think we
need to put the colon in there and then if I
just console log again, you don't need to
do these console logs I'm just verifying that I've got the
right data here. Here you can see
yeah, you've got the list of nodes right here. Actually let's go in
and go nodes articles, refresh over here and now that removes
nodes from the front, so we've only got articles, we've got an array of
just article objects. That's all we're after let's go and put in as our
parent component here, the layout we've already
imported it from where it exists and then because we're accessing
asynchronous data, let's put in a suspense, I'm going to put this all into a container and then
I've already set up a class for the article grid which is going to
be very similar to the product grid and then very similarly to
the product grid, I'm going to run through
all the articles, run map on that and
then for each article, open up this return and we don't need to
put the return in here, I think we can just
put in a div here, put in some JSX here, and let's return
the article title. Here we go and we have access
to the article title here, so if I hit "Save" on that, refresh over here, nothing is coming through at
present, let's have a look. The article grid
is coming through, but not the article title here, and maybe I shouldn't
have put this there. Let's do that see
what happens, Yeah, so there you go we've got four divs here with
all of the titles. We could do that list
like I showed you before, but we're going to turn
these into grids very soon so let's just
leave them as divs. I can create a
separate component in my components
folder over here, but I'm only going to use this on this particular blog route, so I'm going to
just put it below, and I'm going to
call it Article grid item and I'm going to pass through the
article as a prop, and then let's return
the exact same JSX as we do have here, and then so I'm going
to replace here with article grid item. Then the only thing
we need to do is pass through the article as a prop and that's going to
pass that right into here, which we can then use
within our JSX down here. Hit Save on that and we should get the same
result, which we do. What I'm going to do is
add this class here to enable this to use
our CSS article grid item is the class name. Then we're going to
insert two links here. One an image link and
one a link of the title. Have we imported link and
image? No, we haven't. Let's import link and
image from hydrogen. Then I'm going to put
a link component here. I'm going to add in string here. We're going to nest the
article within a blog route. Like we did with products, use the articles
handle as its URL. We'll set up that route
in just a second. I'm going to give that a class
name of image container. Then inside here, let's set
up our image component. All we need to do is pass
through the article image and Shopify's image component will handle the rest apart from alt, which we need to put
through right here, article, image, alt, text. Cool. We don't want it
to close like that, we want it to be self closing. Sweep. Let's just
see if that works. Yep, we've got these
images coming through. Beautiful. Here, I'm going to create
another link component, linking to the same place. I'm going to give
this a class name for the CSS of
article-grid-item-title. Let's close this, so we can see. Close that and then put the article title
in-between these link tags. Save that and hallelujah, we've got our article grid here. If I click on one of these, it's going to just
basically go to a blank page because we
haven't set up that route yet. Let's set that up right now. Just like we did with
collections and products, we just need to create a
new folder called blog. Then just like we did with
collections and products, set up a dynamic route
[handle].server.jsx. Here is where we'll do the bulk of our work
for this lesson. Let's export a default function. We're going to call
this component article and we'll pass through
the article as a prop. In our return statement here, I'm just going to output our layout file and then we'll figure out what to
put inside that in a bit. We need to import
some things here. I'm going to expand this out into multiple
lines like we've done throughout this class and import a few things from the
Shopify hydrogen library. I'm going to need
to useShopQuery. I'm going to need
useLocalization. I'm going to grab the SEO
component because what's the point of having
a good article if it's not search
engine optimized. I'm going to grab
the GQL and I'm going to grab the
image component. I'm also going to
import suspense from react and import the
layouts components of course. I think we need
to go back twice, going to components and then
layout.server. There we go. Next thing we're going
to build the query. Let's do that in the
graphical interface, so we can fix up any errors
before we actually run it. We also take advantage of the auto complete
doing it this way. I'm going to get rid of that
old one and let's just call this query article singular. Before we open that up, actually we need to pass in what variable
we're going to pass in? We need to say that we're
going to be passing in a variable called handle, that is the type of string. Then we're going to target
the specific blog of journal. Then what we need to do is
specify an article by handle. It's not called article
for some reason, it's called articleByHandle. That's pretty specific, but obviously we're
going to put in as the parameter our handle. Open that up and then in here, what are some of the things
that we're going to need to retrieve from each article. Obviously the title, the
date it was published at, the author which
is now authorV2. Which is actually a selection, we just need to specify what
we want on that selection. I just want the name
and then image, which is obviously a selection. We're going to grab the URL and the alt text and then
the content HTML, which is the contents
of the article. Obviously this is not going
to work because we haven't specified the value
of the handle, but at least we have
used graphical to help us auto complete
some of these fields. Let's go back here. I'm
just going to write down here query as a constant GQL, open this up and
then here we go. We have hard-coded the fact that we're looking
inside journal, but the handle we're going
to pass in as a variable. Inside of here we can grab the handle that we're looking
for through the parameters, through the props
of this component. I'm going to grab the handle off the article and then
let's useShopQuery, so const data equals
useShopQuery. This time I'm just
going to put in the query and the variables. The variables that
we're going to pass through is simply the handle. Like I always do,
I'm going to console log the data that's
returned from our query. When we go to one of these, we've got an error, cannot
destructure property handle of article
as it is undefined. We don't actually have access
to an article object here. Let's have a look. We're using the
wrong thing here. We need to useRouteParams. Sorry about that.
UseRouteParams. We not going to pass it through, we're just going to
use the RouteParams. I'm going to useRouteParams
here and pull off the handle. Let's refresh. Oops. Let's go back to our blog. Click on one of these. Now we've got an error
from the Storefront API, the variable handle
was provided invalid. It's probably
because I didn't use the brackets here to invoke it. Let's refresh over here. Yes. Now we get no errors. Let me have a look here. We scroll down. There we go. We're getting the console
log of data here and it's giving us back data blog article by handle and then
object inside there. We can actually just copy this and use this for
our destructuring. Obviously we don't need
to use object here, but we can pull off
the article by handle and then our console log
the article by handle. Refresh over here. If I open this up, you'll see we've got this
object come through title, publishedAt, authorV2, image and the content of it. Very cool. That's working now, our Storefront API query. Now we have this thing
called articleByHandle. I don't really like
articleByHandles, so I'm just going to swap that. I'm just going to
call it an article. I don't know why it needs
to be articleByHandle. Then we'll use
article within here. What I'm going to do is, I'm going to open up a suspense for the SEO component let's say type article and then the data is just going
to be the article. I want to make that
a self-closing tag. Then after that
what we're going to do is create this article page, so I'm going to create a dev
with the class of article, page, and container. Then I'm going to have
two parts to this. I'm going to have the article page header and
then the article itself, article page header
is the class name. I'm going to put through
H1, the article title. I'm going to create a span here for the date which we
still need to format it. But let's just say it's article. Let's just put through the
unformatted one for now. PublishedAt, we'll separate
that with a dot and put the article authorV2.name
here for the author name, and then after the
header, we will create, open up an article HTML tag in here is where we'll put
our main article image. I'm going to pass through
the article.image, and then in the alt texts, the article image, alt text. Self-closing tag for the image, and then what we're
going to do is the div for bringing in the HTML. We're going to see this dangerouslySetInnerHTML
attribute again, and we just need to
do this underscore, underscore HTML equals and
then not equals colon rather. Then put in content HTML coming
from the article object, and then I'm going to give
that a class name for formatting of article body. Cool. Let's wrap up that div, and let's see what
happens now when we run this page. Look at this. We've got the title, we've got the date which
we still need to format. We've got the full
name of the author, and then we've got
this image here, which is the main image. If I close this up, you can see that's the first part and then the
content of the article. This is pretty much done. The only thing we really
need to do is fix up this timestamp here. It's pretty **** ugly. Obviously we can't leave
it like that for the user, and also in case
there is no article, I want to put in a
fallback as well. Let me do that first. I'm just going to put
it in a fallback. If there is no article, then let's return with
our layout components. Still using the container. We'll just put in a div
with article not found, and that also reminds me
to build in something to our app component that I
mentioned in the theory lessons, but I haven't implemented in this particular project
which is bringing in the routes component and
doing a NotFound route. We can use this
wildcard selected here. Anything that slips
through file routes and makes it to here, we'll just bring in a
NotFound components. Sorry, this is
quite the tangent, but I just thought
of it then when I was creating that fallback
for the article page, and then underneath here, actually let's do it here. Function NotFound. This is just a little 404
page return without layout, which I believe we
will have to import layout from components,
layout.server. Then just like we did
for the article page, a container with a
div inside that just says page not found. Now if I go to an article
that's not found, so our hello, Let's just put in something
random like that. It'll come back
article not found. If we go on the root route
and type in something random like hello there, it will say page not found. That's a little
bonus for you there. Let's actually update link 3. I'm bouncing around
a little bit here, but it is the bonus video. Give me some slack guys. In layout.server, let's
put in a link to the blog. Get rid of that ugly link 3, and then if I click "Blog", it's going to take
us to the blog, and then if I click here, we'll get to one of our
articles, and so yeah. Like I was saying before I
went off on that tangent, we need to format this date, which is a little bit more complicated
than we would like, but we are bringing in,
use localization here, which is a hydrogen hook. We can use the structuring
to pull off some things here from the use localization hook. What we're going to pull
off is language, IsoCode. LanguageCode is what
we're going to call it, and then country will
bring off the isoCode of the country and bring that into its own variable
called countryCode. This is going to be for
the next function we use. We're going to create
a formatted date, and we're going to create a new international date
time format objects. This is a specification
within JavaScript. This is not react or hydrogen. Then we'll open
up a string here, putting in those two
variables that we've just created that we've just pulled
off of use localization. The first one languageCode, then dash countryCode, and then the next
argument will go and put in an object year numeric, month long, and day numeric. This is all up to you
personal preference. But we're going to
have a numeric year, a long display for month, which I think just does
like if it's January, it's going to output
January completely, and then day is going to be numeric as well,
which makes sense. Then after here, we're
going to run formats on that newly created object
and then run it through a new object,
article.publishedAt. That looks pretty hectic, but basically we
are just converting that publishedAt date into this international date format. Now that we have
this formatted date, we can just go down
here and replace article publishedAt with
the formatted date, and unless we've made
some errors here, which it looks like we have. What have we done? Cannot access article
before initialization. I have made the article variable after I have
written all this code, so I just needed
to move it before, and then if I hit Save
on that refresh over here you can see the long
form name of the month, which is June 3, 2022 from Ben cell or however
you pronounce his name, and there's your article. If we go to other
articles in this blog, you'll see it works
just as well. That's a little
bonus for you guys. The blog page, I feel like
this lesson should be pretty good practice
for you guys because like I
mentioned earlier, we're running through like
just a sequence over and over again of building
these components, grabbing data from
the storefront API using used sharp query to grab the data that we want
within that returned data and then use it
within our component. Then at the same time, bringing in some of
these other components and hooks from Shopify, which make our lives
so much easier. That basically concludes
this class guys. I hope you enjoyed it. Any questions as always
leave them below, but let's jump into the
conclusion now where we'll talk about your class
project. I'll see you there.
15. Conclusion & Class Project: This concludes this class
on Shopify hydrogen. For your class project, I encourage you to create your own custom storefront
using hydrogen. For inspiration, you can
take what we've done so far in this class
and expand upon it. You could build a
product gallery to bring in more product
photos from the backend, create a drawer to
house the user's cart without having them
leave the product page, or build custom sections to showcase your
product's best features. The choice is yours. As always, if you have any
questions or concerns, leave a comment in the
discussion box below, and I'll do my best to point
you in the right direction. Thanks as always, for watching
and I hope to see you again on some of
my other classes.