Loading video player...
Hello, my name is Ankita and welcome to
the Tanstack start course. In this
course, we are going to build and deploy
a production ready application from
scratch using the Tanstack start
framework. The best way to learn
something and truly master it is to ship
something real. So, we are going to
build an app called as start which is a
modern e-commerce application for
selling your digital products. This app
will teach you everything you need to
know about panstack stack including real
routing, data fetching, loaders,
serverside rendering, dynamic routes,
cart management, server functions, SEO
and a lot more. Basically everything you
need to know to understand how to build
a production ready application using
tanstack start. One of the reasons what
makes Tanstack start so exciting is the
ecosystem around it. So throughout this
course we will be leveraging powerful
libraries from the Tanstack start
ecosystem including React Router,
Tanstack query aka React Query and
Tanstack form. You will learn how these
pieces work together and more
importantly when to use each one by
building and deploying an application
using the Tanstack start framework and
following the specific course. You will
learn how tanstack actually works not
just at the surface level but how key
concepts like routing isomeorphic
loaders server functions mutations and
state management fit together in a real
app. You will also learn how tanstack
start differs from other frameworks like
Nex.js and when it makes sense to pick a
framework like tanstack start and when
it doesn't. On top of that, we will be
using Postgress SQL with Drizzle OM for
type safe database access and
migrations. And we'll be hosting our
database using Superbase, which gives us
a reliable production ready post setup.
For the UI, we will be using Tailwind
CSS alongside Shatzian UI component
library. And yes, this course includes
the latest version of which allows you
to create a project using a variety of
different themes and base libraries such
as radics UI or base UI. This allows us
to build clean, accessible, and reusable
components. We'll be using Zord for
schema validation and TypeScript end to
end for full type safety. This entire
app is built on top of V for a fast
development experience and Nitro to
power the server runtime. We will then
deploy our application using AppRight,
an open-source development platform for
developers who like to ship things. Our
goal towards the end of this course is
to not only learn how to use the Tanstax
start framework, but to actually
understand it in depth. So, what are we
waiting for? Let's get started. To
follow along, make sure to download the
tanstack start cheat sheet that includes
the complete source code, all the
visuals used, a special discord forum,
and all the assets and links that I have
used in this specific video. So, make
sure to check the link in the
description below and download it.
Welcome to Start Shop. Today I'm going
to walk you through this modern
e-commerce application built with
tanstack start tanstack router tanstack
query and this is a fullstack react
application that demonstrates serverside
rendering type same routing real cart
management interaction with a postgress
database with superbase and grizzle OM
and so much more so let me show you what
makes this app special it's built
entirely with the tanstack ecosystem
giving us type safe routes server
function and seamless data
synchronization. First, let's take a
look at the homepage. We have built this
beautiful hero section introducing start
shop and below that you will see three
featured products displayed in a grid
layout. These products are loaded on the
server side using tanstack starts server
functions which means they're available
immediately when the page loads. Notice
how smooth the navigation is. That's
because Stanstack router handling our
type safe routing. Now if you click on
the products page or go click on browse
products. This page displays all
available products in our catalog. Each
product card shows the name, price, the
title, inventory status, and you'll also
be able to add to car. Each card also
has this nice little hover effect and
clicking on any of the product will take
us to its detail page. All of this data
is being fetched using tanstack query as
well as tanstack start server functions
and we'll take a look at how it all
works together provides us with caching
background updates and optimistic
updates. The initial data is loaded on
the server side for fast initial loads.
Then pans query takes over for any
client side updates. Let's click on the
product to see its detailed page. Here
we get a full view of the product with a
large image, detailed description,
price, rating and so on along with
inventory status. Also press add to cart
which automatically adds the item to the
cart. Now notice the specific inventory
status indicator. It just shows if that
specific item if it's in stock or not.
This is all managed through our database
schema. Now here's where things get
interesting. If we click on add to card,
this action is handled by a server
function. The card is stored in our
postquest database, not just in browser
local storage. This means your card
persists across sessions and devices.
You're using superbase to store all the
information. Panstack start also
includes SWR scale while revalidate
feature. Update the card information.
For example, I'm going to say we we have
two products now. And if we go back, you
can already see the cart count has
changed to five. 2 + 2 + 1. If I say two
more, if we go back here, it has changed
to six. That's because of the stale
while revalidate feature. Updates
immediately. This is tanstack queries
optimistic updates working with our
server functions to provide instant
feedback. Now at the bottom of each
product page, we also have a list of
recommended products that dynamically
load based on the product. It uses React
Suspense and the use hook from React for
a smooth loading experience. If you
refresh the page, you're not going to
see a suspense boundary because the
server is so fast, but it also gives us
a smart loading experience. Let's check
out the shopping cart. Here we can see
all the items we have added. We can
increment the count and you can already
see it's incremented. On top of that,
it's also updated with our database. So
if you refresh the page, you can see the
count update here. We can also decrement
the count and remove the item completely
as well. For each item, we can adjust
the quantity using the plus and minus
icons or type directly within the input.
Each change is immediately synced to the
database through our server function.
Also watch the subtotal and the total
update in real time as I change
quantities. The sub the order summary
shows the subtotal and the total all
calculated dynamically. We can also
remove an item entirely. Again, this is
all persisted in our database. We
refresh here. It's gone. And there are
four items here. If we completely remove
everything, we also get a nice empty
state with a call to action to browse
the product. We can also add to card no
matter where you are on the page and
it's going to persist for example. Now
one of the coolest features inside of
this application is the ability to
create a new product directly from the
UI. So here let's navigate to create
product and create a new product. Here
we can add panstack cooler hat. Let's
say very a very cool hat. Price would be
56. Let's add some image. Badge will be
featured and it is in stock. Now if we
create a product, if we go all the way
to the bottom, you will see panstack
cooler hat. The hat information is over
here, the image and so on. Now this form
uses panstack form which validates each
field in real time. So if I add a
invalid name and try to submit, nothing
really happens. The validation is
powered by Zord schemas ensuring type
safety from from the form all the way to
the database. If we go back to the
products, we also see the latest product
updated in real time. When I submit the
product, the product is in fact created
using a server function and townstack
router as well as react query
automatically invalidates the products
query cache. This means when we navigate
back to the products page, our new
product appears immediately without
needing a manual refresh. Now let's
highlight all the technical features
that makes this app really powerful. We
using server functions. All data
notations go through tanstack start
server functions giving us type safe API
calls without writing separate API
routes. We also get complete type safe
routing with full TypeScript support.
Every route is type checked at compile
time. We also get optimistic updates for
instant UI feedback for tail using the
tail rev validate feature. Every page is
server side rendered by default. Initial
page loads are server rendered for first
time to first bite. Then the app
hydrates for a seamless client side
experience. This uses a real postquest
database with drizzle OM for type safe
database queries throughout the app. The
navigation is instant and smooth because
of timestack router that handles
pre-fetching and caching. So moving
between pages feel instantaneous. The
header is sticky for example and has a
nice glass morphism effect. It also
syncs the cart count which gives us
constant feedback about user shopping
state. The entire app is fully
responsive as well. So you could
immediately see how the cards go from 3
to one and so on. So to conclude, Start
Shop gives you a complete e-commerce
application demonstrating modern React
patterns with the Tanstack ecosystem.
This is a productionready foundation
that you can extend with features like
authentication, payment processing,
order management, and more. So thank you
so much for watching. To continue
supporting this channel, please press
the like button and hit subscribe if you
want me to create more content just like
this. And don't forget to check out the
cheat sheet if you want to follow along
with all the links the GitHub repo and
so on to build with me. All righty let's
start building. So what is tanstack
start? Tanstack start is a fullstack
react framework built on top of the
tanstack ecosystem. The tanstack
ecosystem had have a lot of different
libraries such as tanstack start which
is the framework router query table db
ai form virtual pacer store dev tools
and I'm sure there is more cooking right
now. So if you go ahead over to the
tanstack start it is a full stack
framework powered by the tanstack router
for react and solid application. Instead
of being a single monolithic framework
tanstack start is designed to be
composable. It brings together a variety
of different features. For example, it
brings together routing, data loading,
service execution, and rendering in a
very explicit way. So you always
understand what's happening and why.
Now, Tanstack start gives you the entire
gives you a lot of flexibility to build
your React applications the way you
would want to. So there's less magic
involved compared to frameworks like
Nex.js JS where a lot of opinions are
made for you and in sansex start it's
more so that you have to make those
opinions and this is an very extremely
and this is an extremely important this
is a very crucial detail that you need
to remember so you have to configure a
lot of it yourself but they give you all
the tools necessary so you just sort of
like pick and choose from a different
pieces of you pick and choose based on a
different piece so you pick and choose
what pieces you want it's kind of like
Lego boxes here Anstack start provides
you full document SSR streaming server
function bundling and more thanks to
weed. Now before we get into weed, we
need to understand that tanstack start
is built on top of two key tech. One is
tanstack router and one is weed. Now
tanstack router is in fact a type- safe
router for building web apps with
advanced features like nested routing,
search params and data loading. And
here's why I have so much confidence in
Tanstack start. Because even though
Tanstax start the framework is in the
release candidate stage right now,
Tanstack router is already in production
and it's being already used. You can see
the number of npm downloads here.
They're going up by the second. So at
the heart of Tanstack start is the
Tanstack router. It isn't just
responsible for managing URLs. In
panstack start routes define the way
your app is structured. How you navigate
between the routes, how layouts work,
data loaders, error boundaries, search
params, and when the code should run on
the server versus on the client. This
means that routing becomes the backbone
of your entire application. So instead
of having logic scattered throughout, it
gives you some organization tools so
that your routes are organized which
makes complex apps easier to understand.
Now what is weed? Weed is the tool
responsible for your development
experience inside of panstack start.
It's tanstack start is built on top of
wheat. It powers the dev server hot
module replacement and production
builds. It literally give you lightning
fast HMR which is hot module
replacement. So that whenever you update
any file, your changes are instantly
reflected on the server. So vit is why
your dev server starts instantly.
Changes to show up immediately and the
app feels fast while you're building. So
vit answers a very simple question. How
quickly can I write and iterate on the
code? And tartar does use wheat because
it's modern, it's powerful, it's fast
and it's framework agnostic. That means
anyone any other frameworks can use
beat. Now tanstack start is also we
talked about tanstack router we talked
about beat but tanstack start also uses
nitro. Now nitro is the server runtime
layer extends your vit application with
a production server compatible with any
runtime. So you can ship full stack vit
applications with nitro. Nitro is in
fact your server runtime layer. So when
you write things like route loaders or
server functions inside of panstack
start that code needs something to
actually run it on the server and that
is in fact it was job. So nitro will
handle all the server side stuff such as
executing server side code handling
requests and responses and it's in fact
this plugin inside of the v config file
and we'll take a look at that later.
Nitro also answers this question. How
does this app actually run in
production? It's not but it's also
important to understand that nitro is
not hosting your app. It's the engine
that runs your server code. So you would
definitely need some hosting platform
like apprite versel cloudflare and so
on. The next tool the next tooling
that's extremely important in the panac
ecosystem is vg. Now vg is where
everything comes together. In fact if
you want to ship your own framework you
could basically use vi here. You could
build your javascript applications and
frameworks with ease. It's the full
stack JavaScript SDK. It's the
orchestration layer that connects read
Nitro and makes full stack rendering
possible. You can use Vit for
development and builds, Nitro for server
execution, routing and rendering across
server and client and Vinci is the one
responsible for all of that. So you
don't necessarily interact with Vinci
but know that it's the blue that make
makes Tanstack start work as this
cohesive system for example. So the real
question here is what makes Tanstack
start so powerful. The real power comes
from how these tools are combined. So
first of all everything is explicit. So
you always know where is data loaded,
where the code runs, how routing affects
rendering and so on. It's production
ready per by default. That means you're
not just building a demo and then
upgrading later. From day one, you are
interacting with real routing, real
server execution, real deployment
patterns and so on. Plus, you get 100
full type safety. So if you create a
route that doesn't exist, then sex start
is going to throw an error. So your
application is now going to break. Now I
have over 300 plus lessons on the Nex.js
course platform and it's built on top of
Nex.js. But any I remember before Nex.js
CS supported a feature like this. Even
linking to a route broke the whole thing
because I have more than 300 lessons.
Sometimes it's hard to keep track if I'm
linking it the right way. So this pug
feature is like really powerful. Plus it
gives you type safety. So should I use
tanstack start or just tanstack router?
Now 90% of the framework usually comes
down to the router and tanstack start is
no different. But tanstack start relies
100% on the router. But there are a few
powerful features that tanstack start
gives us. It gives us full document SSR.
So by default every single route inside
of panstack start is going to be
rendered on the server side. Although
you could configure it, this gives you
better performance and SEO. You can also
configure SEO for your routes as well.
Just like you would in Nex.js with
metadata functions. You also get
streaming. So you get progressive page
loading for improved user experience.
You get server routes as well as API
routes and we'll talk about the
differences a bit later but it allows
you to build build your backend
endpoints inside the framework alongside
your front end goal. It also gives you
server functions and you need to think
of server functions as ways your
application would interact internally.
So if there are internal things you have
to use server functions. They're
essentially type save RPCs between
client and server. You get middleware
and context. So you do get powerful
request and response handling. Full
stack bundling. So that means it's
optimized. It creates optimized builds
for both client and serverside code. You
can deploy it to any framework possible.
Any provider possible deployed to any V
compatible hosting provider. Now here's
the thing with Nex.js. It was in a lot
of controversy for how it's heavily tied
to Versel. Obviously over the years that
story changed but with tanstack start it
is not tied to anyone. It's in fact you
can deploy it to whatever read
compatible hosting provider if you like.
So that is amazing that it's true it's
open source in a true sense. You also
get end to end type safety across the
entire stack and this is very powerful
because it protects you from shipping
those gnarly bugs uh inside of your
application. So with great power comes
responsibility as well. So definitely
there are some limitation. It does not
support server components yet and with
with the recent react server components
vulnerabilities. I mean it's not a bad
thing but they are actively working on
integration. But I know that analyst
Linsley was the creator of this entire
dance stack ecosystem. He also
explicitly said on X that now that these
vulnerabilities are exposed, he's going
to take extra precautions on how he
creates react server components inside
of Tanstack start. But otherwise,
Tanstack start provide the same
capabilities as other frameworks, even
more features and a powerful developer
experience. And because T standstack is
100% open source, free to use and always
will be. It has no affiliation with any
of the companies, it's awesome, but it
also has a web community, great
contributors, and also a variety of
sponsors that make it happen. So that's
extremely important. So to summarize,
Tanstack start gives you the structure
and stack router defines how your
application is going to be organized.
Ved makes your developments fast. Nitro
runs your serverside code. Vinci is the
one that ties everything together. So
once you understand this specific mental
model, build building with tanstack
start is going to become very powerful
for you. So now let's go ahead and
create our tanstack app. So we'll be
heading to tanstack.com and click start
from here. Click on getting started and
let's click on getting started from the
left hand side as well. You could start
a new project from scratch using the
tanstack start CLI which is what we
would be doing. But they also have a
variety of different examples if you
want to try it out. So if you click on
tanstack start CLI there are two few
options. So let's just go ahead and copy
this or press this copy button. What
we'll do is we will clone and run the
basic example right away. If we just go
to this specific example but personally
I just like to start from scratch. Now
if you can see there are a bunch of
different examples here. They provide a
stack widths preview to find one that
feels like a good starting point for
example and they also haven't deployed a
netify button if you want as well. Again
net nifi is another cloud provider likel
apprite and so on. All right so let's go
ahead and get started. So here let's
just press enter and this is how you
could configure your start application.
What would you like to name your
project? Now let's call this tax shop.
Let's go ahead press enter. Let's call
this tax shop
app.
Feel free to call it whatever you like.
Then yes, we are going to use tailwind.
And here we could just say yes, I want
ESLint. You can pick a specific
deployment adapter such as Cloudflare,
Netifi or Nitro. We're going to go with
Nitro to be agnostic. And this is where
you could pick a variety of different
adapters, add-ons uh to Tanstack such as
Neon, VGOS, clerk, convex, entry, so on.
So you can see there are so many over
here. We're not going to go with any of
these because we want to just set up
something from scratch. And I think the
best way to learn something and not get
confused is by just starting things from
scratch. Would you like any examples?
No, no examples here. All righty. So now
it's going to install the dependencies
and dev dependencies installed anstack
router and any of the other dependencies
needed also obviously react any of the
dependencies needed for bootstrapping
our application. There we go. Now let's
just go to stack shop app and then open
it up in cursor. Now you'd be also just
go directly to the editor of your
choice. I'm going to be using cursor for
my example, but you could also just go
to the editor of your choice and open
this project as well. All righty. So now
we have quite a few projects ready to
go. Now let's go ahead and start the
development server. Just say npm rundev.
As we do it has started a server here
and you can see wheat and you can just
go to localhost 3000 and there we go. If
this is your first tanstack start
application, congratulations. you have
created your first app. Now that we have
our application created, let me go ahead
and walk you through the project setup.
So first of all, we have the stack
folder where all your files are going to
be compiled. VS Code for how the route 3
would work and we'll get to that in a
second. Then we have the public folder
for all your public assets. Robots.tsx
favicon. Now favicon is in fact in the
app folder in Nex.js app router. So keep
that in mind if you're migrating from
there. We have a robots.txt txt file for
our SEO robots. Then let's walk through
each file here. So first of all, let's
go to router.tsx. Now router.tsx in fact
creates our router here. So it gets
create router from tanstack react
router. It creates a router instance and
this instance is what would be used by
the tanstack start framework. The
default preload state time is zero.
which means if you should use any
preload stale time or not. You could
also change it to 30 seconds and the
stale time for this for routes would be
30 seconds. But again more on that
later. Then you could take a look at
route tree.gen.ps.
Now this is a file that anstack start
creates or transact router creates that
has a mapping of all the files folders
and routes inside of pansstack
framework. This gets generated so that
whenever you try to link to a route that
doesn't exist, it can tell you that.
Then we have a status rptsx and this is
where we have the import at tailwind
wherein tailwind directive is in fact
installed here. All righty. So now let's
go to the routes folder. Routes folder
has two files now index.dsx as well as
root.tsx.
index.tsx is very similar to page.tsx
inside of the app router. anytime you
route to it. This is the default route.
But keep this in mind. If I close this,
we are exporting a route called as
create file route and the component here
which is the app component. And that's
exactly how you create a route inside of
anstack start. Now these are all the
different features here and we're just
looping through those features here. So
there's nothing exciting here that's
going on. But keep in mind create fire
route is very important. Then we have
the underscore root.ts. tsx file. This
specific file is sort of like the
layout.tsx inside of Nex.js that sets up
your HTML and body elements such as
HTML, the head tag, header component,
tanstack dev tools, scripts and so on.
So if you take a look at the head
content, head content scripts and create
root route all of them come from React
Router. Tanstack dev tools come from
React router dev tools. and stack router
dev tools also come from React router
dev tools and so on. Now this head
element is extremely important because
this is a root route. We are create
using create root route versus if you're
creating a regular route you would just
use create file route. Again the naming
is honestly amazing because if you see
how it's beautifully designed you're
going to fall in love with tanstack
start. Then we have this head element
that I was just talking about a second
ago wherein this specific head element
allows us to set up all the meta tags
inside of our application. For example,
if we rename this to stack shop and go
back here, you're going to see that now
the title has changed to stack shop
here. That's exactly how you set up your
application. That's exactly how you set
up the title. If you want a description,
then you could do the same as well.
Stack shop is a platform for buying and
selling products. And this just extends
the meta tag inside of your head to add
a description. If you go to browser
developer tools, go to head, go to
title, we have stack shop. But if you go
to description, we have stack shop is a
platform for buying and selling
products. This is exactly what we just
set up. Then we have links for
stylesheet for example and the shell
component is this root document. Now
like I mentioned this is similar to how
layout is set up. It in fact inside of
next.js it wraps your entire application
because it contains the HTML head and
body elements and all the children would
be the children routes in your
application. Then the scripts would be
any scripts you would add inside of the
application. Now if we head to data
you're going to see that this is a
server function that you can create
different types of functions inside of
tanstack start. One of them is a server
function that basically creates a HTTP
endpoint with a get method. Now if you
als you can also create a post method as
well and this would in fact be a
mutation. Now but server functions are
not just limited to post like it is
inside of Nex.js. It's in fact get. It
could be get or post because they're
server functions. So they are HTTP get
and post calls here. Then if you go to
components, we have the header fire
which again is a simple react component.
Now if you want to link between
different elements, you would just use
the link component from React router
here. Now one powerful feature of the
link component is that let's say we were
to go to panstack router here. Let's
open up browser developer tools. Go to
the network tab. Select all. And now if
I open this up, you can see that when I
hover over these elements or if I scroll
down, nothing is loading here. One of
the powerful feature of the link
component is that on hover, it's going
to preload the upcoming route that you
you might be scrolling into as you enter
a browser's viewport. So for example to
enable that feature we need to go to the
so for that if you go to the router file
that we had here and here let's just say
default preload as intent this is going
to preload all the links for example. So
here if we go and open up browser
developer tools go to the tab you can
see that when on hover these pages are
already loaded and that's the link
component inside of panst start it it's
quite powerful it's very similar to the
link component inside of xjs and as you
are about to scroll more items if they
have links in it that specific page will
get preloaded so when you navigate to it
the navigation is quite instant there's
one more file that is extremely
important important inside of the
tanstack start ecosystem which is the
vit config file. Now v config is amazing
because it lets you configure your how
your vit server should behave how your
entire application should work. So you
can see read uses this plug-in system so
you can plug and play like level pieces
what you want and what you don't want.
For example you want panstack dev tool.
So it's a plugin. Here we have nitro
here which allows us to spin up our
server runtime. We have config paths
with the sconfig JSON. Tailwind CSS is
also a plug-in. Tanstack start is a
plugin. V react is a plug-in and so on.
If you want to configure tanstack start
further, we will be making some changes
in this file as well. This v config file
is extremely important. Components is
where all the components will be stored.
data is we what we already saw the data
we have this server function here then
we have the eslim config file that
allows us to configure how our
application should lend you we have
prettier which gives us a common config
for how our application should compile
for example semicolon is false single
code is true trailing comma is all and
so on now feel free to configure how you
want your application to format on save
then we have the ds config file how
typescript should compile and so on.
Those are all the different files inside
of panstack start. Let's talk about
routing now. So when it comes to
routing, there are different ways you
can route inside of tanstack start. And
since built on top of tanstack router,
whatever routing logic or fundamentals
that apply inside of tanstack start come
directly from the router. So if you have
any question related to the router, I
would recommend checking the
documentation of the router instead. So
let's talk about the index route. First
of all, we have already taken a look at
this, right? We took a look at index.tsx
that allows us to create a root route or
I should say a index route, a default
route, this specific folder. And one
thing I should also mention that
Tanstack uses a filebased routing system
as well. And based on the way you name
your files, it is automatically going to
route to it. So first of all, as you can
see, this is the index route. Then we
have the root route for example. But now
let's say if you want to create a brand
new route, let's call it as products
because we would need it eventually. If
we call it products, then if we create a
index.tsx route, this means that anyone
could route to /roucts. So the minute we
do, you can see this code automatically
got populated because tan stack start
already knows when it comes to file what
you're up to. It's a bit of juju here,
but what it does is it it knows the
location of the file where if it's a
folder and if it's an index or tsx file,
then it automatically autopop populates
that code. So again, create file route
is a function here. Remember we talked
about this and this is where we say /
products is the route we want to expose
and what component it should map to. So
here we are calling hello products. Now
let's go to taxop and now let's go to /
products. Now as we do you can see hello
products is right here. That means a
products route has been created. That's
amazing. What about dynamic routes then?
You may already be wondering because if
you think about it, we have indexed out
but we also have dynamic routes then.
Well, in order for you to create dynamic
routes, you need to create another file,
not a folder like you do in next.js, but
you just do dollar sign ID and ID
becomes a slug of for that specific
dynamic route dot DSX. Now this just
means it will route to / product/ id and
this is the ID of your application. We
could just use route dot use params and
this is a route constant that we
declared. It's magical. It also has
things like route use loader data. We'll
take a look at that later. But now you
could simply just point to it and get
the ID from the route. So now if we go
to slash one, we say slash one. If we go
to whatever then we get whatever and so
on as well. So like this is a dynamic
route inside of panstack ecosystem
panstack start. All righty. So now we
learned about index route. We learned
about dynamic routes. Then we also
talked about links wherein we could link
between the products as well. So for
example, if we go here and then add a
link component from tanstack react
router, we have to use the two attribute
and link to any of these for example. So
the fact that one doesn't exist, we just
have to do something like this where we
say products ID params ID 1 ID 2 ID3. So
now if we go back here and refresh the
page, you can already see the link. So
if I click on it, it goes to ID 1. Click
on the other ID 2 and click on the
third, then ID3 for example. And that's
how linking as well works. Now let's go
ahead and clear some of the files we are
no longer going to need such as these
demo this demo folder. Then if we go to
index.dsx,
we won't need any of this. So let's just
say the main page. Maybe we can link to
products. import link from panstack
router. Remove this unused constant.
Remove the unused imports and so on. We
can remove this logo or SVG file inside
of data. We don't need this server
function at all. We can keep the header.
We won't need any of these at all. For
example, so we can just keep a simple
header component.
And let's call this stack shop.
Now let's remove all these imports and
unused functions for example. All
righty. So now you can see we have stack
shop. When you click on products, it
goes to products and these products goes
to individual products. That's the basic
setup. Thanks to the tanstack ecosystem,
Delvin is already installed. As we saw
earlier, we already have a plug-in in
the V config file for Tailwind, but we
don't have SHAT CN UI right now. So for
that, we are going to go ahead and
install it. And the latest update of
SHTN allows you to create a new project,
apply a bunch of styles, pick a
different component library or an
existing preset for the component
library, for the style, for base colors,
fonts, and so on. So what we could do is
go ahead and pick this and create a
project. Now I have already gone ahead
and create pick a few defaults. For
example, I chose base UI nova stone
color as rose huge icons because we have
lucid tabler and huge. If you know I
always go for lucid but this time let's
just try some other icon library some
other icons font as fig tree radius
medium menu color default bore and if
you press this it's going to change all
your changes because it's going to go
with the default theme or you could
start over. So now if you go ahead and
create you have three options nextjs
tanstack or wheat. If we go with
tanstack with this specific preset it's
going to create a brand new project. So
what do we do then? So what I've done is
I have already picked these defaults and
set up shnui. So let's just go to get
view components get started and and
let's head on to the installation
section here. Let's pick tanstack start.
Let's copy this command because we
already have a project. Stop the server.
Paste this. So now it's saying I need to
create components JSON to add
components. Let's just say yes. And this
is where it already chose a framework as
standard tail in CSS config import
alias. Let's just go with stone. And now
it's going to update the CSS variables
in style.css.
Now it has added buttons for us. But we
could also go ahead and add card as well
because we need a card component for the
product card that you see on the page.
All righty. So let's reset the server.
And now let's go through exactly what
happened here. If you go to components
UI, you will now see a button. DSX as
well as a card. DSX which is amazing. If
we go to components.json, JSON. So this
is the description from SHAT CN. Shad
CNN UI references this specific file to
figure out what exactly it needs to
install. So now we need to change a few
things here. So let's change that. First
of all, I changed it to base nova. I set
tsx as true. Change the base color to
stone. And this is basically the new way
of creating a project. So just reference
that in the cheat sheet. For example,
the icon library is huge icons and so
on. Now, if you just want to go with the
default styles, that's also totally
okay. Or you could simply just go with
this one. Then on top of that, we are
also going to need a few packages. We
are going to need the following
packages. Base UI react font source as
fig tree huge icons free core free icons
tail and whest
installed the packages. Let's restart
the server. So whatever we have defined
here, the icon library and style and so
on. Because of the packages we
installed, it can actually understand it
because we're not using shad CN UI
anywhere. So let's just go ahead and use
it. So let's go to index and here let's
add a button component and let's just
say click me. Now you can see that the
new button is over here. Now we also
need to update this CSS file. So if you
go to styles or CSS, what shats and UI
does is that it sets up these different
variables and then these colors. Now
these colors are not what we want. We
want some different colors. Plus we also
need a couple more packages here. So
let's just paste what I have given you.
So we have tail in CSS, animate CSS,
chat and tail in CSS and font source as
tree. And these are all the different
variables that we have changed. It's
referencing the font sands as fic tree
and so on. All right. So now let's just
restart the server because we have
changed a lot. The specific import is no
longer needed. So we can remove it. And
now you can see that click me is in fact
configured as per the colors we just
provided. That means it works. Now let's
stop the server and let's add button one
more time as well as card because we
have now updated the styles just to be
safe. Restart the server and still
everything works. So now this is the app
that we're trying to build. I put them
side by side so we could make changes on
the left and see changes on the right.
So open up our development server here.
So first of all let's just set up header
because right now it's looking super
basic. So, in order for us to set up a
header, it needs to live in a file that
every single route touches, which is
going to be our underscore root.x file.
This is where we already have the
header. So, no matter where you go, for
example, you go to products or product
one or wherever, you still have the same
header. That's exactly the header we
want to create. So, here, let's just
call it start stack shop. Let's call
this start shop. And then let's add
another link for products. Let's add for
not all of these but just for creating a
product to equals create product for
example. Now as you can see that editor
is already complaining that this
specific route does not exist. Even if I
do this it doesn't exist. That's because
this tanstack ecosystem is fully type.
It gives you type type safe routing. For
for example, if I press slash, you're
going to see all the routes that are
available. The fact that create product
route doesn't exist, it's complaining
about it. So now let's comment this and
let's see what we get. Now we get start
shop and products. Obviously, we need
some spacing here, but you get the idea.
Then we also need a /cart as well, for
example, which again does not exist. So
let's just comment it out for now, too.
So now let's style the header. So first
of all we need to change the background
color here. So let's make the header
sticky top zero Z 40 so that it's
overlaying everyone else
border slate 200
and then bg 80 and then we have backdrop
as blur. Let's remove this backdrop as
blur. In dark mode, it's going to be
border slate 800 800. And dark mode,
it's going to be bg slate 950/
80. There we go. And let's give it
another div that will wrap all the that
will wrap our logo, which is going to
have a flex item center gap three. Let's
add our logo. Move it over here for
example. And then the rest of the links
will be inside a nav something like
that. When it comes to the nav, it's
going to be item center gap 3 text smium
text slate 700 and dark will be text 200
and from small onwards it will be flex.
And now we have the linking to the
products which is going to have a class
name of rounded LG Bx3
B Y1 hover will be bg slate 100 and dark
hover would be BG slate 800. There we
go. So you can see it has this nice
little hover. We also need another div
inside the logo itself because something
is off there. So first of all let's give
flex flex call. So we need a link to to
wrap the entire element. So if you see
here we have this logo icon and a text
text smold
text slate 900 dark text white. There we
go. And now for the link of flex items
center gap 2. And inside that it's going
to have a shopping bag icon. size will
be 20. And this specific shopping bag
icon could be from Lucid React. All
righty. We have the nice icon. Now, the
shopping bag icon has this nice little
background. So, let's add that too.
Center justify center rounded excel BG
slate 900.
part will also be PG white
text slate 800. There we go. Let's wrap
this nib up. Good. For example, this
needs to be wrapped all the way at the
bottom. And now the styling has been
fixed. So now we see this. For example,
let's go to root and for the children,
let's also create some styles because
this way we can give it some sort of a
padding to the entire application here.
Let's wrap it. Let's give it a class
name of MX auto max wxl
4
and py 6. There we go. So now our main
elements also have a nice little nice
padding. Padding horizontal and padding
vertical. And for the header as well,
min green screen 950
dark text white. Wrap this up. And also
move the main inside here too. There we
go. So this entire container is now more
contained. And now if you go to the
header wxel
tx4 py3 item center justify between
let's also give it a div here. There we
go. So now it has a nice little spacing
here too. Perfect. So our header
component is pretty much good to go. So
now we could add a home. So let's copy
this and let's add a root for home
example. We can see home right. And then
we also need a create product and a
cart. Now for now these two could simply
be now for now let's just create it and
let's keep it as is over here. Now when
it comes to the cart itself over here
you can see how it looks like. For
example when you expand it these three
links disappear and when you go to
mobile we show the cart with zero count.
We also hide the price here. So how do
we implement that here? Well, first of
all, let's just create a separate div
like we did here and wrap it.
Then we are going to give it a flex item
gap 2. Now there's a bit more gap. Then
for this specific class name, it's going
to be inline flex. And before we add any
more styles, let's just give a span to
the card text.
And let's give another span for some
count over here. And then let's give
another span for the total. So it's
going to be like 10. And let's give it a
dollar sign as well. Something like
that. So now let's style it. So, it's
going to be item center
gap 2 rounded full border border slate
200. Then for dark or I guess this PG
white px4 PY 2 the text small like extra
small font semib bold text color would
be 800 shadow SM
transition and then when you hover over
it it's going to be negative translate Y
0.5 on hover it would be shadow MD as
well. There we go. So, it's looking way
better. Now, for the card text, we're
going to keep it as is. But for the
count, we're going to give it flex H6,
min W6, which will give it a min width,
item center, justify center, round it
full, BG slate 900. Then we can say px2,
for example. Text will be 11 px.
font bold text white and so on. Now it's
as you can see the count is looking
really nice. And lastly for the total
subtotal it's going to be by default
we're going to hide it on mobile and
it's also going to have 11 px font would
be medium text late 500 and from small
onwards it would be inline. There we go.
So now if I go to mobile the subtitle is
hidden. Now at the same time we also
want all these three links to hide other
because there's no space here. So let's
hide it on mobile and on desktop let's
show it which we already do shown
otherwise it's hidden and so on. And we
don't need this on the nav or I guess we
don't need to add the cart in the nav.
We have these three links. We can move
this div outside the app. Now we could
say it.
There we go. So now if I close it, you
can see how it looks. And if we go all
the way to the bottom,
we have we need to move this
div outside of this other div as well.
We really want it at all the way to the
end. So it's going to be right at this
level, which we do. And all we need to
do here, add flex. There we go. So now
that fixes it. And this is how our items
are going to look like. There we go. And
now obviously over here we have
different formatting which we'll get to
once we hook up the cart. Beautiful. So
our header component is looking very
nice. We can remove this empty fragment
too cuz we no longer need it. And now
when it comes to these links themselves,
let's just keep these errors because we
know that we're going to resolve it. Now
let's build this specific section here
because we want this to be on only on
the homepage. So now if you go to
index.dsx, that's where this section
would be. So here for the this specific
div, let's give it a space y12. And we
need a few different elements. We need a
B tag that says your favorite e-commerce
store. Then we need another H1 which
says start shop, your one-stop shop for
all your needs. And then we also need a
link component to products.
And let's use a arrow right icon from
Lucid React. And the size is going to be
16. So now we have all these elements
and we need the text as well which is
going to be browse products. We're
adding the styles the layout here all
the elements that are needed and now we
could wrap it up. So first of all we
need to wrap this entire thing into a
card component from shad cnui. So we can
just wrap this up entirely. So now this
automatically gives us this background.
So we don't need to style it further.
And then right here we need space Y4 and
a MD limit the width to max 2X. Limit
the width to max the W2XL. So something
like this. Now let's add this in a
section because this is going to be the
top section over here. And that section
would be MX auto and going to give it
flex max W6 XL flex.
Now, this one is going to be a card
title. So, we could just get the card
title from SHTUI
and wrap it just like that. And this
card title is going to have text for
Excel font bold or I should say SP bold
leading type text late 900 and dark
white. There we go. This entire card
needs a P8 as well. And it also has this
nice little shadow. So, let's add that
too.
We go. And it's going to be BG white 80.
It's looking really good. Now, for this
specific text, it's going to be text SM
because it's really small. Font semi
bold leading, but I guess just don't
need leading type. We just need
uppercase. Tracking wide
text blue 600. There we go. So, you can
already see it's looking a lot better.
for example. Then we have the H1 here
which is going to be text for Excel and
so on. Since you already styled it, we
don't need to style the H1 separately.
Definitely need some padding over here
which we can fix. And now we need a
button or like a link that looks like a
button. So this one could be in the
footer for example or alternatively we
could just make this a description.
There we go. But in this case for the
link we are going to have inline flex
item center cap 2 rounded pull and make
it look like a button. BG slate
900 TX5
BY3 text SM font 7 bold text to white
shadow large transition hover is going
to be negative and so on. All righty. So
now this button is also looking very
nice as you can see here. Now for the
the div for the entire page then I have
space Y bg linear to B giving it some
color stuff and that gives us a nice
gradient to white via white to slate 50
then E6 so on we can reduce the spacing
cuz there's a lot of spacing and that's
it just the fact that we used a card we
don't need to add any spacing Here we
go. P is small, B is capital. Honestly,
little things like that. Sweet. All
righty. So now our card component and
there's a typo here says browse product.
So our card component is looking very
nice. Now let's just remove this because
we don't want to limit it limit the
entire section, but rather just limit
the text. So here we could say 6. Let's
just remove this entire thing, the fact
that we have a card. So now this car
expands. But if you take a look, we
still want this to be Excel. Limit the
width there. So for that, let's just
limit it to Excel. There we go. Now it's
looking really good in desktop as well.
Perfect. Now there are a variety of
different ways you could load data
inside of Stanstack start. I would like
to put all of these concepts together
under data fetching. Now we'll go over
each one. You could depending on your
use case. Landstax start gives you the
flexibility for where you want to load
data. For example, there are API routes,
server functions, middleware, server
only function, client only function,
data only, static server function,
isomeorphic loaders, and so much more.
Let's talk about isomeorphic loaders
now. So, this is extremely important and
it's a core fundamental inside of
panstack start. Understanding where the
code is going to run is fundamental to
building panstack start application.
Every route has something called as a
loader. So if you want some data that
needs to be fetched at the route level,
which means the minute you type a route,
you want data to be available for that
specific route. Before that route is
rendered, components are rendered, you
would need to call loader. But here's
the tricky thing. Route loaders are
isomeorphic, which means they both run
both on the server and the client, not
just on the server. Since a loader is a
route level function that fetches data
before the route renders. Now why do
loaders exist? Well, a lot of modern
applications need data before they
render. For example, product pages in
our case need data, need products before
the page renders. Dashboards need
metrics even for SEO data. This is
really good for serverside rendering as
well because as I mentioned by default
tack start serverside renders the data.
Now traditionally this type of loader
logic would end up in a use effect
inside of the component. We would have
to do duplicate fetches on the server
side as well as the client side. There
are too many loading states. So loaders
exist to centralize data fetching at the
routing level. So in this case you just
pass in loader fetch the data and that's
it. Now in terms of the execution
boundary pans application runs in two
environments. First and the server
environment is the node runtime which
has access to file system databases and
environment variables and during SSR
initial page renders on server API
request which is server functions
executing server side buildtime static
generation and pre-rendering. The second
environment is the plan environment
which is our browser after hydration,
navigation, user interactions and so on.
Now if you want your application
function to only run on the server then
there's a server only function which is
very similar to the server only package
inside of Nex.js. That means it will
only run on the server and on the client
it will crash. There's also create
server function which again you can call
it from the client as well. There's
client only and client and there's so
much more. But let's take a look at how
they create loaders first. So first of
all, in our case, what we are going to
need is these three products need to be
fetched at the loader level because I
want this data available for the
recommended products before we load the
page. So for that, let's just go to
index.tsx. And right now, we don't have
anything listed here. We have a /
products page. Let's look at the index
or tsx over here where we have our
section in here. These three routes need
to be loaded. Three products need to be
loaded beforehand. Let's remove the
unused imports. And now over here we
need to call loaders. Now the best way
to do so is right underneath this is
where we need it. Let's copy it. Paste
it. There we go. So now this is going to
run during serverside rendering and on
client during navigation. So slash API/
products they don't exist but if you
take a go to fake store API for example
/ products then that exists. So for
example let's get the response.json and
return the products. Now the the way you
would access this loader data inside our
application is by using route bird use
loader data and we have taken a look at
this before. So now let's add a console
log data here and a console log products
here. Now let's refresh the page and
you're going to see this data that's
available and there is a lot of data. So
let's just get data slice and now let's
clear the console. Let's refresh the
page. And as we do we got four products.
This is a log for example. So you can
already see that we on the client side
we got 19 products. On the service side
we got four products. So declare it to
refresh it. Now if you go back here we
got four products. 1 2 3 four. If you go
back here let's just get the first
product for example. And now you're
going to see that we only got one
product here. And if I clear it go back
here. Refresh it again. And now you will
see that we got 1 2 3 4 5 6. This is all
the data possible which is the specific
route but then it returned only one
product here because we are returning
all the data here and then returning
only one product here for example. So if
we add server data and here let's add
client products. Let's just return zero
product so you can see it. Refresh it.
And now you're going to see server data
one product and client products as one
item as well. So this is how you could
work with loaders and you could just
call use loader data here. So now let's
just seed some data here because
obviously we don't have the database set
up yet. Let's just seed it. So first of
all if we go to DB let's create a new
folder here and inside of that let's
create a seed.ts file and let's call
this sample products. These are a bunch
of different sample products with name,
description price badge rating
reviews, image, and inventory.
We're storing these products here. These
are the same products that we show here.
Every product has a new badge or it has
a price, rating, number of reviews, the
image, and inventory could be in stock
or back order or pre-order. So, what we
could do is again let's export it and
then load that here. So instead, let's
just return sample products from the
seed. Let's refresh the page. If you go
back here, these are all the different
products that the client is returning
just like we saw. So now we could simply
render it on the page. Now, as you can
see here, we have recommended. We have
these two title and description. And
then again, we have card component. So
now let's create another card component
here. Let's create another section here.
here another section here and let's
create another card component here which
is going to have a bunch of information
such as a badge card and so on. Before
that this entire thing is also in a
card. So let's just create a new card
here. Let's add a card header which is
going to have a P tag and let's call
this recommended. Then we are going to
have a card title which is going to be
starter picks from the catalog. Then we
are going to have a card description
which is going to be curated items. And
then right underneath would be different
cards. So if you go back here this is
how it looks like. Let's give it the
same padding. Now for recommended let's
style it. It's going to be very similar
to your favorite e-commerce store. So
you could just copy this it. that's
recommended instead. This one would be
XS. And then for the card description,
it's going to be text smate
600. We also have a
link component here as well, which goes
to /ash products, and it's going to have
the same styling as before. And this
time we're going to say view all. And
then we'll have an arrow right icon,
which is going to be 14. Now this one,
let's hide it in mobile.
It's going to be item center. Y 2
rounded full
border border slate 200 px4 py2 text
access font semib bold text slate 700
and because it's hidden on mobile we
can't see it. So let's say inline flex
here you can see the new wall button
here. Let's see transition power would
be again the same thing so on. Now we
have all these items. So let's move this
in two divs. So one div and this is the
second div. So that we could give a
justify between. Then another div here.
It wraps the whole thing. There we go.
And now here we could just give it
justify between. So it renders all the
way here. Now you can see the starter
pics is a little bigger than what the
card title is. So we need to also style
that. Excel orange semibold text late
900 and so on. So now if we go back here
you can see the title is making a lot
more sense. And if we make turn it into
a mobile device then it disappears. And
for some reason the description the
others have this TX4 title. So let's
just say px0. There we go. So now it's
aligned correctly. So it's the same as
this one now. Perfect. So now we could
add more cards underneath it. But as you
can see, this in fact is going to be
repeated code for the card component. So
we could just create a new component
called as product card and render it.
All right. So now right here, let's give
it another div and let's give it a make
it a grid of gap four small will be
calls two otherwise not. And then in
this case we can loop through the
product get the value here. So now you
can see we refresh the page we have all
the items displayed but as you can see
we don't want to return all the items we
just return want to return three. So we
have done so it over here we only want
to recommend and let's say there are
bunch of products you want to pick you
would always ob always do so but this is
how we're going to do it. We're going to
slice the products and only get the top
three. But here's the thing now. We
could obviously create a product card
component now and then style it further.
For example, give it a descript content
and then description as well. Render
that. For example, render the price and
all that too. All that fun stuff. That's
awesome. We could definitely do so. But
let's talk about the data fetching
strategy here for this specific
recommended route, these recommended
card. But when if you think about it,
yes, you these are serverside rendering
by default and they are isomeorphic
functions. We also want them to be
pre-rendered. So for example, we talked
about the execution model. We talked
about different functions here. But
there are also another type of functions
called as the static server function.
And they are experimental. So keep that
in mind. But what static server
functions do is that they executed at
build time and they are cached as as
static assets. when using pre-rendering
or static generation. So we could just
make this this specific call we can make
it a static mode by applying the static
function middleware to pix server
function. Now let's talk about server
functions. So if you go back to server
functions documentation they let you
define server only logic that can be
called anywhere in your application. So
for instance in this case let's say the
load loader data we want it to call from
the server function and we could easily
do that loaders can call server
functions components can do hooks or any
other server functions too. They run on
the server and can be invoked from the
client side code. So in order for us to
run the server function let's copy this
and then we could just aait call it from
anywhere we like. So for example, if we
call this, then this would return a
message called hello from server and so
on. But where do we call server
function? Let's get to that in a second.
So first of all, we could simply create
a new server function here and server
function comes from anst start package
and then this runs only on the server.
So now if I refresh the page, let's also
call this get server time. Say get
server time. We don't need to await it.
So you can see here we could just await
call from anywhere and get it. So we
have this async function and now we
could call this get server time. Now
await expressions are only allowed
within async function for example. So we
could make app async and right if I
refresh the page you don't want to see
anything and let's just add maybe as the
title just this date for example. And if
I refresh the page when it comes to
server time you can see that the time
keeps changing. So that's the idea with
server functions. You define the server
only logic and then you return it from
anywhere. But here's the thing. Server
functions also take a method. This is a
get method for example and it still
works that by default it's a get method.
But what if I want to post something?
Then I could change this to post. And
now we could this would be a mutation.
So we could invoke it. So for example
create server function would be a post.
And here we could simply just return it
and so on. So now if I refresh the page,
it's no longer going to work. It's going
to break because this is no longer a get
request. This could also be a post
request. For example, if you're trying
to add something, create, update or
delete that is performing any crowd
operations and so on. So now if I
refresh the server, it still works. If I
refresh it again, it still works as well
even though this is a post method. So if
you want to perform a mutation, you
could easily call this. But this means
that now this is only a serverside code.
It is no longer going to run on the
client. This is a server only logic for
example. And it will run on the client
in a sense like you can invoke it but
this is its own function. It's not going
to run on the server and on the client
as well. That is in fact loaders. But
now where do we call server functions?
We can call them from route loaders. We
can call them with the use server
function hook. So for example, this is a
server function and if you want to call
it, we could just simply call use server
function hook. We could compose the
server logic. We could handle form
submissions and so on as well. The
server functions accept a single data
parameter since they cross the network
boundary. Validation ensures type safety
and so on. So for example, if we want to
restrict it, then we could easily do so
by adding a input validator as well.
This way we are saying that there needs
to be a name and then it's going to
return hello data.name. We could also
use it with zod which we will do
eventually and add more input
validations via a schema here too. And
this is exactly the use case where we'll
be using later wherever we are going to
create a product. You could also
redirect a user from a server function
as well. So let's say if there is no
user or the user session is no longer
active, you could redirect it. You could
also throw a not found error as well in
case you want to throw that for example.
So server functions are extremely
powerful inside of tanstack start. So
now inside of our application, let's
remove this. And we have a loader here
that returns the sample products. That's
it. That's all we need. Let's restart
our server. And now let's refresh the
page. Let's remove this time. And we are
back to where we were to render the
three product cards. However, product
card doesn't exist yet. So, let's just
go ahead and create it so we could use
it. So, inside of components, let's oops
create a new file called as product
card. DSX. This will simply be a product
card. We don't have a type yet, but we
will create one later. Or we could just
say these are all the different values.
For instance,
there was something we could just say
product and then this would be the type
of it all. Let's import card component
header title and so on. You don't need a
separate import. We can just import card
title here. All righty. So now let's
just remove this entirely. render a
product card and pass a product and a
key. Now, if we go back here, let's
update the card component. We don't need
a key here anymore, but we do need to
import card content and card
description. And we are back to where we
started, which is now that this is a
separate product card component. Now if
we take a look at the type we have name,
description price badge straating
reviews, image and string. So now if we
go back here, we definitely have an
error because batch is optional. So
let's make it optional here. For
example, not everyone would have a
batch. Now we get the product name and
so on. Then just create a structure. So
first of all, you have a card header. We
have a card title which is going to be a
text large. This card title for some
reason inside of Shad's TN are not that
big. So we need to make it a bit bigger.
Then we have a card description. We have
card action and that action will have a
product price. Product price that would
be a dollar sign. These two in fact
would be part of the header. There we
go. And now we are going to have the
card content. And in this case we need
to style it a bit differently. So, for
example, we're going to have the
inventory if it's in stock or not. We're
going to have the rating. We're going to
have the reviews here. So, first of all,
let's set that up. So, we're going to
have a span. This will be product dot
inventory. Then, we are going to have
two more of these, which will be rating
as well as it will be reviews. Right? If
you go back, it has reading and reviews
all next to each other. So we are going
to format that. Now let's give it a flex
item center justify tween. For that this
is going to be a div. The first one
which is going to have a flex item
center gap 2 text small text slate 600
and alongside the ratings would be the
reviews as well. So we need to say these
many review for example and the class
name for this would be font semi bold
and the class name for this would be
text slate 400. All righty. So now we
have the product inventory over here and
for this we will have rounded full
border px3 py1 text excs font salam
bold. There we go. So now this inventory
is also looking really nice. But now
depending on the type of inventory we
need to say a specific thing. So if it's
in stock we need to say in stock.
Otherwise if it's back order we need to
say back order. Otherwise we need to say
out of stock or pre-order for example.
So that's the logic for the inventory.
So now we have these three items here.
Obviously they don't look exactly the
same. And this one actually semantically
it should be a P tag not a dim cuz it
has a couple of spans here and so on.
All righty. So now we also need these
two brackets here around reviews. Now if
it's in stock or not we are showing the
these different colors. So for that
let's define object is going to be
called inventory tone. Based on the type
of the object, we will then see, for
example, if it's in stock, then it's
going to be emerald, emerald 50. Text is
going to be text emerald 600. And border
would be emerald
100. For back order, it's going to be
amber. Amber, amber. And for reorder,
it's going to be indigo. There we go.
This is going to be
700 and 700. Perfect. So now chat CN
also gave us one more thing which is a
utility file. So if we go to utils which
is in s in lib this specific file lets
us merge a bunch of class names. It uses
a package called as tail and merge that
allows us to bunch merge a bunch of
class names. Otherwise, if you just use
the back text syntax, it may not merge
it correctly and may not override. So in
that case, we are going to use that
package to merge a bunch of styles here
and then pass it and let's give it a
type. So now you can see install is
looking really good. Now we have the
card content. Let's add the card footer.
Card footer will have the add toart
button. And here we are going to have a
span which is going to be product price.
And let's give it PT0 for example just
like we see here. And I have a product
price and a button which is going to be
a chart and button. And here it will say
we'll use a shopping bag icon and size
will be 16 and we'll say add to cart.
There we go. And now in terms of before
we add the class name, let's add size of
SM smaller variant secondary for
example. And and in terms of the class
name, we're going to change it to 900
and text white. And then hover is going
to be this add to card button. All
righty. So now let's also give it a flex
of justify between as well because this
needs to be here. All righty. So now
when it comes to the button itself, we
need to give it a onclick handler. It's
going to be add to cart. We also want
this entire card to route to the product
page, individual product page as well.
So first of all, we need to wrap this
inside of a link component. Wrap this
entire card. Let's say from link
component from time start and just pass
that in for example. And if you see if
we click on it, if we click on our
application, if we link it to the unique
ID for example, the ID doesn't exist
yet. So let's just link all our
applications to zero for now. I click on
not this one. But if I click on our
application, it goes to the zero ID like
one or whatever. So now we also want the
add toart button to be clickable as
well. So the solution to this is to call
E which is available here dot stop
propagation as well as E do.p prevent
default. This way we could still call
add to cart without having to link it.
So if I go to the console we do see add
to cart log add to cart so on and I can
still click on the link component and it
works thanks to the e do. stop
propagation. And lastly, in terms of
styling, style a product price. It's
going to be large and font semi bold.
There we go. So now you can see this is
looking really good. But we don't want
this top border here that we see, right?
We don't want border at all. Border top.
So we could say border top will be zero.
There we go. Now the border is also
gone. And bg transparent. Perfect. So
now our add toart button is also looking
very nice. Now obviously it's not
looking really good. So let's fix the
remaining styling here. So to the link
component here we're going to say we
have cursor pointer each full shadow
smover would be negative translate try
one
and hover we also have shadow
LG
now you can already see it's getting
bumped we just need to add transition so
that smooth just like that and we can
remove this shadow because it's making
it look a bit weird. All righty. So now
when it comes to the actual card itself,
we need to add some layout spacing as
well. So here we can say space Y4. So
now it has some equal spacing here. Then
max w 6XL MX auto
rounded 2 XL. But the card is taking
care of that. So we don't need to worry
about There we go. So now if we go back
here, we have we need to have all of
them in just one card. Right now it
looks like they're not. So let's move
this div inside here. There we go. And
now it's looking so much better. Just
like this one. But obviously there are a
few things that we missed in the product
card. So let's give it a gapple 2 and
also give it a font semi bold. And right
here will be the batch. If there is a
product batch then display it. And in
this case not give it let's remove this
autogenerated code. And let's add a
badge code. We can see rounded
full bg slate 900 px2 py 0.5 text excess
font semi bold text white and so on. And
let's give it a div here, which is going
to be flex item center gap 2. And you
can see it's looking so much better.
It's just that this one still has more
spacing. So let's give it a bit more
spacing here. You can say px 4 py 4.
There we go. Similarly for the index,
this one would be bx. There we go. Very
nice. All righty. Our card component is
looking really good. So, we could simply
just repurpose the same card component
everywhere else, too. And since we're
displaying, we don't need to display the
price in two places. So, we could remove
it from top right. There we go. All
righty. So, now that we our cards are
set up, we could work on the product
page next. When you link to it, this is
the product page. Let's talk about that.
Let's focus on creating the products
page. Now in the products page if we go
here go to browse products we go to the
products page in here we have oh a list
of cards. Now we could simply use the
same card component but the more
important part is what is the data
fetching strategy we are going to use
here. First of all we can again load all
the data using loaders as well. But
because we're going to need some sort of
caching we could also introduce React
query here. React query would allow us
to fetch the data and then we could
store that data and reuse elsewhere if
you like. Now to be very honest with
you, React Query for storing products
data could be an overkill. However, if
you want to cache your data as much as
possible, then you could definitely do
something like this. So let's go ahead
and create our
route. Now we already did that. Remember
if you go to / products that exists
already. So, if you go to index.tsx,
that's exactly how the start looks like.
Now, the title of this the start shop
catalog and all that, it's very similar
to how we just set up recommended
products. So, we're not going to spend
too much time on this. We're just going
to copy what we had
in here.
Just copy this and paste it inside here.
And now let's import
car title, description,
and so on. All right. So now obviously
we don't want this to be this big. We
want it to be smaller. And we don't want
browse products at all. So you can
remove the actually we keep the
description, but we remove the link. For
example, let's call this start shop
catalog. Cool. So first of all, let's
just go to the index.psx page. And here
we have this card. Let's copy this card
component. So, we just cop get the
section and the entire card. Paste that.
Now, let's get all the imports. I don't
need two imports of the same thing.
Let's just import it from
card component from shad cnui lucid
react. We don't need this at all.
Perfect. We don't need a view all button
here. We just need this. Now, this could
be t shop catalog. This could be the
title and this would be the description.
There we go. So, now if we go here, you
don't need to make this blue or
anything. We can just keep it as is. We
can also give it a class name here. Just
going to be space y 6. This is going to
be one section. And then this is the
other section where we would be
rendering our products. In terms of
this, we just need to make this smaller
and text laid to be a 500 color. We can
just remove this color. And that's it.
There we go. It looks very similar. And
let's remove the board here. All these
elements. Let's add space one. So
there's a bit more spacing. Beautiful.
So this section is ready to go. Let's
remove the unused on boards. And now
again, we're going to need loader. And
loader will also be a bunch of sample
products. But this time we're going to
load all the products. And again we are
going to create a products.m map and get
the products and again get the products
from routes.loader data get index here
call product index as the key and so on.
There we go. So now if you see we have
all these products displayed already. So
our products page is also ready to go.
However, if you're wondering well this
is great but we need to do more. Instead
of now calling the classic loaders we're
also here to learn obviously anx. So
let's create a server function here to
load the product. So you can say fetch
products equals create
server function
and the way you would do that is by
calling a method called as get and now
call the handler and return the
products. Now in inside the loader we're
not just going to return the products
inside the loader we are also going to
call fetch products for example and then
we get the products from loader data and
display it. So now if you see there's an
error here a warning more so that a not
found error was encountered on the route
with id root but a not found component
option was not configured nor was a
router lever default not found component
configured. So let's just go ahead and
configure that. So if we go to router
here we could say default not found
component and simply add not found.
Similarly, we could add a default error
component and add a error as well. So,
let's add a not found component here and
then not found H for example. So, now in
this we can just simply replace this
code and
give the user a link to go back to home
for example. So now this specific
component will be called whenever there
is a not found in any of the routes. So
for example, if I throw not found from
react router for example and if we go to
home this is inside products. If I go to
products we do get the not found page
that we just set up because we just
threw a not found error. All righty. So
now if a page for some reason has
doesn't exist we could simply just do
so. Let's talk about middleware. What is
a middleware inside of panstack start?
So I can go back to the start
documentation and go to middleware.
Middleware allows you to customize the
behavior of both server routes. What is
something that needs to be called before
your route is invoked? For example,
verifying a user's identity before
executing a server function or checking
authentication authorization logging
collecting metrics, error handling, all
these are really examples of muver. And
there are two types of middleware. One
is a request middleware and a server
function middleware. A request
middleware is used to customize the
behavior of any server request versus a
server function middleware is used to
customize the behavior of server
functions. You could simply add dot
middleware and add a logging middleware
just like they show here. And then this
specific middleware will be called each
time. So and always make sure that
middleware is next nextable which means
that you must call the next function
otherwise we are going to run an
infinite loop because we need to we need
to pass the control back to the router
by saying next. So execute what's next.
So let's implement our own middleware
now. So the best way to do so is first
of all add here a server call a server
object and then call middleware. The
minute you do so this is now a server
middleware and then if it's a server
route which we haven't created yet but
we will. It's a server route then we
could just call the logging middleware.
But if in case of a server function
middleware it has extra functionality
for server functions. So we could say we
could call client and dots server. So we
can validate the input, we can call the
server and so on. There's a lot to go
over in middleware here. Now let's focus
on adding our serverside middleware. So
first of all, let's create a object for
server and let's pass in middleware and
this is what this is one. It would be an
array. Now this specific middleware has
nothing to do with server functions. It
is for routes. So if you want to create
a route here, you could just simply add
handlers
and then pass a get request which would
return whatever we want. So here we
could say get or we could say post for
example and a sync and we get a request
object here. So we could do whatever we
want here and then we could simply do
create a product if you want. So we
could simply return
a JSON from tanstack react start and
let's just say hello world here. In fact
we could also just get request JSON and
again this is a regular request API from
that we know. So this is exactly what
request would be and then we could say
hello world from post request. Now how
do we exactly invoke this? Well, we
could invoke it by using an application
here. So now let's say I say products
and now we get hello world from post
request with empty body and I'm simply
calling / products with a post request
and that's the beauty of it. Like
literally in the same file we created a
server function that could be invoked
inside of our loaders before the content
to be available before the page is
rendered. Then in the same file we
created a API route specifically which
is a post request. We had access to the
request again colllocating it with the
react component and so on. So that is
just crazy like how amazing this entire
architecture is. The more you build the
better you're going to get and
understanding the pros and cons.
Similarly, if you want to create a get
request, you could do so as well. And we
could just say hello from you will do
just say hello world from get request
for example. So now here if I change
this to get and call this you get hello
world from get. Now similarly we could
do the same with get request. So you
could say get request and again it has
access to a bunch of stuff but we can
just simply run JSON which again comes
from that third and if it says hello
world from get request. Now if we go
back here change this to get invoke it.
Now if we invoke it there is no request
object inside of get we can pass that
but now if we invoke it we're now going
to see hello world from get request we
made a mistake which pen which where we
passed in the request object. So we can
still access request object here. It's
just that we can pass it as a JSON. So
can still do whatever we want with
request here just like you normally
would. So now we have a post request
here. We have a get request here. If I
refresh the page, now you're going to
see that this specific API route has a
get request with hello world from get
request. Now this makes sense, right?
Because when you call / product is in
fact a get request. The fact that we
added this has replaced our page
entirely. So let's go back to it because
we definitely need it. But this specific
post is very handy because we can still
call the same API route and with a post
request which is not something you could
just directly invoke by accessing /
products
because you need to create a post
request on a get request and then create
a product for example as well which we
will do so very soon. But now back to
middleware you need some sort of a
middleware. Let's create a logger
middleware. It's very handy to have some
sort of a logging middleware for logging
our API requests. Now, in order to do
so, let's create a we don't need to
export it. Let's create a logger
middleware will be create middleware
from panstax start dot hander actually
not handler dots server. And in here we
going to have a async with request and
response for example with request and
next. This needs to be next request. And
now we are going to log every single
request that comes in. So we could type
request url and we can say from request
do headers for example get. We could
also do or user agent or origin and then
we always need to make sure we return
next because if we don't the next thing
is not going to work like if the next
router won't execute. So now anytime we
haven't we we did hook the middleware
here. So now any time a request will be
made logger middleware will be invoked.
So if I refresh the page over here you
can see logger middleware. Now let me
refresh it again. Go back here. We did
invoke logger middleware for example. So
right now the origin is null but the
request URL is the URL that we're
requesting from. So this specific logger
middleware is going to first get invoked
before our route is going to get invoked
for example. So that's just the power of
adding middleware. So that's how you add
middleware. And that's all for the
products page. Like I mentioned, you
want to cache this products data because
we don't want to fetch it every single
time. Now, tanstack start router does
have its own cache. Like I mentioned
earlier, if you go to the router,
default preload state time is zero and
that's because we were going to use
something like react query or like tack
query, but we could set this to 30. That
means we're saying the default preload
time a route should use if no pre if no
preload stale time is provided. We could
change this value. However, we want to
cache it. We want to leverage the
importance of tan query. In fact, even
when we'll add to card and this will
work. We'll increment it by one over
here and build a card page as well. Then
again the same thing we want to now
update this count get this data and not
having to call the server every single
time we want to cache a lot of the items
too. So in that case we could use
tanstack query for that. So if we go
back here we can just set up tanstack
query. So now if we go here and go to
tanstack query this is how we set it up.
And if we go to quick start you will be
able to see exactly how to set up the
query. TSA query is really powerful and
honestly in this specific application we
would be scratching the surface but this
is a very good example for how you would
use query inside of panstack start. All
right, so let's set it up installation.
Let's copy this. Head to the terminal
and paste it. Let's install it. Let's
restart the server. And there's also a
ESLint plugin for catching bugs. So
let's add that too. Paste it. It's a
development package that's why hyphen d
restart you start the server and now we
need to essentially add a query provider
and wrap it in and the way you would do
that inside of anstack start is also
very similar except you would be doing a
couple things differently. So now if we
go to the router the root actually over
here we'll be wrapping our entire
application
with query client provider and then wrap
it over here with query client provider.
But here's the thing. Query client
provider means query client. And you
would get query client byruct
dstructuring it and calling it from
route dot use route context.
And this is and now we have our query
client. However, we still need to
configure it further. All the way to the
top. We have create root route. Now we
need to do create root route with
context. And now extend it. Now we are
going to have send it. It's going to be
something like this. And now query
client doesn't exist yet. But we need to
import that from tile stack query to
wrote this. First of all, we have not
imported provider. So we can just get
that. But this one would be type of
query client. There we go. And that's
all we need to get anstack query here.
And lastly, if you go to the router now,
suddenly you're going to see that there
is an error here for example. So we just
need to pass context query client equals
new query client from react query. There
we go. So now query client error has
disappeared. We could already see that
you know the fact that we didn't set it
up properly initially or there was one
step missing the type system complained
already. Now if you go to root let's
also
set up react query dev tools which I
believe we installed or it's called
panstack query
and this doesn't come from here but in
fact it comes from this package and I
could not find the modules so let's copy
this. All righty. So now React query is
set up. So we could simply just go to
our products page and use it. So right
after the loader data, we could create a
use query. We could invoke it and then
give it a key called as the products
key. And now let's just get fix the
import. And instead of fetch products
and now we can just say fetch products
which comes from the server function
here. And then for initial data we could
just give it the loader data. And now
instead of looping to products we could
loop through data for example and give
it a question mark. All righty. So now
if I refresh the page it still works. So
if I console log data inspect element go
to the console I get data array of eight
which is exactly what we want. So a
react query is set up as well. Let's
talk about selective SSR which is
selective serverside rendering in
tanstack start routes matching the
initial requests are rendered on the
server by default. We know this by now.
This means there is a before load
function and a loader that are executed
on the server followed by rendering the
route components. The resulting HTML is
sent to the client which hydrates the
markup into a fully interactive
application aka serverside rendering.
But what if you don't want to opt into
serverside rendering? You just want to
opt into it for specific routes. You
could easily do that inside of tanstack
start. There are three different options
in which you could configure your route.
First of all, you could say I don't want
SSR at all. disabled SSR by default and
you could when you're creating a start
when you're creating start from this is
a solid example but let's put a react
for example and over here if you say
create start function from react start
if you say default SSR is false then SSR
will be default however you could also
opt into SSR on per route basis now SSR
is true by default for every single
route but let's say you don't want that
you don't want that at all for specific
route then you could just set SSR equals
false. This will disable the SSR and
also the execution of routes before load
and loader and then it would be like a
typical client component that gets
rendered on the client. We we don't need
server side rendering. The third option
is data only and this is kind of neat
actually. This hybrid option will run
the before load function on the server
and send the resulting context to the
client. It will run the loader on the
server and send the loader data to the
client and it will disable serverside
rendering of the route component. What
this means is it will execute on the
server execute on the client execute on
the server executing on the client. So
let's see how that behavior works. So
first of all over here on the route
level at the route level if we say SSR
equals data only let's watch what
happens if I refresh the page. This is
data only. So you're going to see that.
So this is specifically used when the
data is loaded on the server. The before
load function is called as well as the
loader is called and the response is
then sent to the component. However, the
entire HTML is not generated on the
server. It disables the serverside
rendering of the root component. So now
you can see over here if we refresh the
page as we said data only there is that
blank page. However, if I disable it and
reload it, it already has it cached.
It's loading it instantly from the
server. So that's essentially the
difference. On the other hand, if I say
I don't want SSR at all for whatever
reason. So now you're going to see that
it's all client side is no longer on the
server. So it's no longer on the server
at all because it's by default. So now
if I console log here loader loader is
not going to be logged here at all. So
if I refresh it, nope, there's no log
for loader. However, if I remove this,
refresh it, go back. Now you can see
that loader does have the log here. We
do see loader log here. So you could see
the three different types of serverside
rendering or selective serverside
rendering inside our panstack start. Now
in our case we need SSR. It's nice for
our application. It's great for SEO as
well for returning all of this data. So
we're just going to keep it. Now when
you click on any specific route, it goes
to a dynamic route which says hello
product. That specific route hasn't been
set up yet. So if we go here, click on
the route. This is how it looks like.
Again, this is a card component. And at
the bottom, we also show recommended
products here as well. And these
recommended products honestly are all
products at this point. However, you
could just recommend them as needed and
tweak them as needed. So, let's build
the specific page now. Let's work on
building the dynamic pages. Now, if you
take a look, this is how it looks like.
Have the back to products link. You have
title, description. Again, similar data,
right? But here is also where you could
add to card. Save to later doesn't do
much, but you could definitely add that
as a feature if you want. Then we have
all these recommended products. Again,
if you take a look, if I refresh the
page, you're going to see that first
this data loads and then the rest of the
data gets loaded by showing some sort of
a loader here. And we'll replace it with
a nice skeleton as well. So you could
see that. But again all this to show
that how Tanstack start makes it so easy
for us to pick and choose the different
data fing strategies how it makes the
framework makes it easy for us to just
build applications and it honestly it's
a lot more fun let's build the page now
so here let's go to the ID page and if
you click on any of the individual
dynamic pages that's where we land
similar idea let's use a loader to load
that specific page So now in this case
we are going to get async and then get
the params from the loader and this is
where we could do return and if we take
a look at index right now we're getting
the sample products. So here we could
simply just get sample products find and
then get that data. Now if you take a
look at sample products at this point we
have we don't have an ID yet. So maybe
it's time for us to integrate it with
the database because right now let's
simply just working with sample
products. So enough of that. Let's use
database for now. Let's use a database.
So in this application we are going to
be using drizzle and post SQL alongside
superbase to set up our entire database
and its ecosystem. So first of all we
need to get all of these packages. It
also has this specific guide here. So
you could go there. Now let's copy this
and install it. Now Drizzle OM and
Postgress allows us to write powerful
SQL queries. If you haven't worked with
an OM before, the way it works is that
instead of you writing raw SQL queries,
it will give you an API, an interface
for you to just execute specific
functions and it's going to do the job,
the tricky, the hard part of writing
those queries for you. So that becomes a
lot easier for you if you are not good
at SQL but you just want to get things
done. It's performant as well because
you may not make mistakes and it will
figure out the most efficient ways to do
so and drizzle is quite powerful. So now
let's just copy this and head over to
the terminal and paste it. Then we also
now going to initialize the driver by
creating an index.ts file. But then as
you can see since we are using
connection pooling via superbase this is
exactly what we are going to set up. So
now that we have these two installed
let's go here and go to DB and create a
index.ts file. In this specific file we
are going to import drizzle from let's
restart our server now drizzle/
node postgress.
We are going to get pool from pg and we
are going to import from schema. Now
schema doesn't exist yet but we going to
create it. You're also going to say if a
database URL is not there set it and
then create a new pool here. Now
connection string will be the database
URL and we're also going to say SSL
would be that process
n dot database URL not SSL. base
database URL has includes
superbase
then reject unauthorized equals false.
And this is just one of those settings
you're going to need whenever you set up
the pooling. And honestly, it was a bit
tricky for me to figure out exactly how
all these individual settings work. But
there you go. There is no PG. So I
believe we have to install it. So we can
say PG as an extension as well. All
right, Super Base is a really powerful
Postgress development platform. It's
built on top of pro Postgress and if you
haven't already, make sure you go and
sign up. The pricing of it is actually
quite generous. Unlimited API requests,
50,000 monthly active users and so on.
So again, it's really awesome for you to
get started. After you sign up, just go
to the dashboard, create an organization
if you haven't already, and this is
where we're going to go ahead and create
a project called Stack Shop app. And
this is where we're going to select a
password. We're going to get it to
generate a password. Let's copy this. We
definitely need it. I'm in Europe right
now. So that's where that's the region
I'm going to pick. And let's create a
new project. All righty. So now our
super basease project is getting spun
up. Now the other thing is press connect
here immediately and instead of direct
connection what we want is a session
pooler cuz that is extremely important
for us. If you don't select this and if
you click on direct connection you will
not be able to connect to the database.
That's just the limitation. So now we
have this database and you see this
password here. That's exactly where we
are going to paste the password that we
just generated. So before we do
anything, let's just save the password
here. Copy the URL. And now create a new
file called as env. Now again just like
you have we have it in next.js. Same
idea you create an env file. Then you
could use process.n the name of the
environment variable to access it. So
here we could say database URL equals
this file. And now this specific
password let's just replace it here.
There we go. Perfect. Now we have
superbase
and we will be using the schema and
result to generate the table and insert
documents in the table as well. Insert
rows in the table as well. So for that
let's just go ahead and create a schema.
So now I'm just going to give you the
schema and this is how it works. We're
going to use the drizzle OMPG core
package to get all these functions. But
let's go ahead and see how we could
create it. So first of all, this is a
table. It's a PG table, PostPress table.
And we have a products table here.
You're generating the UU ID. You're
generating the ID which is a UU ID. That
means it's going to be a unique string.
We have name, description, price, badge,
and so on. description is text, price is
numeric, we are essentially also adding
constraints that it cannot be null and
so on. We also have an enum for batch
called as pg enum which with these
values. So if you try to insert any
other values other than these values,
it's not going to work. Then we have
reviews which is which is an integer.
Images var. Then we have inventory which
is also an enum which is again going to
be all these values for example. And
then we are exporting two types. One is
a in for select type and an in for
insert type. This way when you're
inserting the record you're not going to
get a type error because ID is already
autogenerated. You don't pass in the ID.
It gets autogenerated. This is why we
actually we have an infer select and
infer insert as separate types. Then we
have a carts item table and the key
thing here is we have the ID and then we
have the product ID and product ID is
the one that references the products
table. So the cards table and the
products table are connected just like
that. And then we have quantity created
at and updated at. And here we have
created at value. And again this is also
autogenerated based on the time stamp
and so on. Same idea here. And then
we're exporting enum value types too.
This way we can use it as a type
everywhere. We don't have to type it all
the time. So that's how our schema
works. So now that we have this, we
could simply run a command
and that will create this specific
schema create the tables if they don't
exist and then later on we'll be able to
see this data as well. So first of all
let's stop the server and run a command
called generate but before that let's
head to package JSON and here let's add
DB generate, DB migrate and DB studio.
This these three commands are very handy
as they will allow us to generate our
schema migrate run the migrations and
also open up a studio which we will do
so in a second. So now let's just run
npm run vizil actually it should be db
gener and as you can see it's complains
that config json does not exist. So we
need to create that. So if we go back to
our documentation for drizzle and go to
getting started superbase, we have more
information here. So we need the drizzle
config file. So now we just need to
create drizzle.config.ts.
And now paste it. Now as you can
see/config
does not exist. We need to install that
as well. So you can see all these
command all these packages that we need.
Right. So let's just install that too.
Drizzle OM we already have. Postquest we
already have env is a package we need.
We also need drizzle kit which we
install as well as tsx that allows us to
run any scripts. So now we have source
db schema. We have the drizzle file
which will do the outputed and then
postress SQL and then this is going to
be the database URL. Perfect. Now let's
just run DB generate and there we go.
These tables have been created. So if we
go here now we have a folder called
drizzle which has the specific migration
to create the different tables. Now what
it what actually happened is it went
through the schema and it wrote these
SQL queries that it's going to run next.
So now in order for us to push it to the
database URL, we just need to npm run.
And if you take a look, we don't have
this yet. So we can just add DB push.
DB push, which will be drizzle push. So
it's going to pull the schema from the
database and changes are going to be
applied. There we go. So now if
everything works as expected, then we
should see data into the database or we
should see tables being created. So if
you go to stack shop app and go to the
table editor now you see card items as
well as products created as per the
schema that we asked for. That means
everything worked as expected which is
awesome. Now let's take a look at seed
data. Right now we have these sample
products. We want to insert them so we
could directly pull from the database.
Let's also start the server as well by
calling npm runab. Now here I'm just
going to replace it with a seed script
and this is just going to get it from
the schema and no ids are generated but
this is the exact same data. However, at
the bottom DB is in fact right here. So
we just need to fix the import. There we
go. All righty. So it's going to simply
just dynamically import the database
modules because this is a TypeScript
file. It needs to dynamically import it
and then it's going to seed the
products. So in order for us to run this
file, it's also given it in they have
also given the same in the documentation
as well. If you we can use the tsx
command that we installed. All we need
is to call npx tsx
and run the command. So now we can say
npx dsx
source to be db and then seed file. And
now it has inserted eight products and
the seed is in fact successful.
So now if we go to Superbase, refresh
it, you are now going to see all these
different products created here, which
is amazing
because we just seated the products. All
righty. Let's restart the server and go
to localhost 3000. And something went
wrong here. Injecting env from env. Just
going to run this command to see what
happened. And now that we have an error,
let's also add a script for seeding the
database, which is just going to be
this. And now it's asking us to run
this. So let's run that. All righty.
Product already exists. So we just need
to reset it. Copy it. And boom, clear
all products. And we set it as well.
Let's run the development server. Let's
start the server. and it stopped again.
Since we saw that error that we just
got, we need to add two things. We need
to add these two lines which is nitro
preset would be node server and node n
will be this or production. Now here's
the thing this specific script is in
fact a production script and the
middleware or tanstack start router is
going to include it as part of the
regular files. So we need to say exclude
that by adding this alternatively plus
we also need to add node n equals
production and node preset node server
and then run the script again. So by
running npm run dbced it works. Now
let's start the server again. And if you
refresh the page it says cannot read
properties undefined cuz remember we
changed a few things in the seed script.
It's no longer exporting a sample
products anymore. So now let's just fix
that by going to index.tsx and we no
longer need to do this. We just need to
get the data now from the result
database from the superbase database. So
for that let's just go ahead and inside
of source let's create a new folder call
it data. And now let's create a new file
call it products.ts. Now here let's
import DB from DB and this DB is in fact
this browser DB here and going back here
let's create a new function then this
would be a async function that would
call get all products here we don't want
to get all the products we only want to
get three products so let's just do so
so let's just say get and recommended
products and here let's just Say we need
const or we could simply add a try catch
block and say db dot select dot from
products and this would be products from
db schema. It's again the same data. So
let's just call this data data and right
here let's set dotlimit as a function to
be three. There we go. And if some error
were to occur it will just return the
error. Now if we go back here we could
simply call await get recommended
products and now return the products.
Now if we refresh the page the server
stopped running but now if we refresh
the page we are seeing an error that
says fetch failed. There are no sample
products. So let's just comment this
out. Comment loader out as well. There
we go. So now let's restart the server.
Go to localhost 3000.
All righty. As you can see, a server
stops running each time it tells us run
with the reset flag. We can't keep
resetting all the time. That's because
we have added this received script in
this folder. What we need to do is add
it inside the scripts folder so that
anstack start knows that hey this is no
no file that you need to include. So for
that let's just create a scripts folder
outside of the source folder and move
this in here. Let's update the import
for example. Similarly let's go to
package JSON
and this is going to be scripts /
seed.ts. And now once and for all let's
reset the DB. Let's run the server. And
now if we refresh the page you're no
longer going to get the error. Now we
are back in business. However, keep this
in mind that the seed script has been
updated. Now, we have moved this data
into source/data/proucts
instead of sample products. Plus, we are
saying that if only run seed if this
file is executed directly, the script
should not only run when executed via
npm dbced. That's extremely important as
well for something for you to keep in
mind. All righty. So now our server has
started with the changes from the
database itself because that's exactly
what we are calling here. So if we take
a look at index we're getting the
products getting recommended products
from here. Let's keep it here for now.
And then we also need now to get the
dynamic products for example as well. So
in order for us to get the dynamic
products, we can just call async get
product by id and this time it would be
db select from products where eq comes
from drizzle as well will be ID and this
ID would be the params ID for example
this one ID itself right and this time
and we also need to change one more
thing which is that in index if you take
a look at product we need to now update
the type to be products select and id
can in fact be product ID because it
exists now we just set that up. So now
we get the DB do select from products
well products ID do id and we say give
me limit of one that means give me only
one match and then give me zero item so
now we can say it's going to be this or
null there we go so now if we go to
id.dsx DSX in here we have the loader
and instead we're going to do instead we
are going to fetch the product instead
we are going to call get await get
product by ID and then get the params
there we go now this ID is one obviously
we need to now get the new ID and the
new ID has been displayed so let's
return some other information such as
maybe just the JSON stringifying of the
product itself and for that we can get
row dot use data and now if I refresh
there we go we get all the information
from the database which is exactly what
we want and let me show you a trick if
you do null comma 2 then it's in fact
formatted not so much anymore but it
gives it two spa like two spaces for
example because the it's just space over
here as a third parameter so it's a lot
more readable than it needs to be or you
can say as well and refresh it and so
on. Doesn't really matter, but this was
just a cool little trick I've been using
over the years. All righty. So now all
our data is in fact coming from the
server. However, we also need to update
we fetching the products as well. We
need to fetch it here. So let's create a
new query now which gets all the
products. So these are recommended
products. We need get all products. So
now let's remove the limit and then
that's it. That's all we need. We need
all the products here. So now if we go
to products index page, we just need to
get all the products. Remove any unused
import and awaited. See if we go here
and go to browse products. They're no
longer calling sample products. We're in
fact getting the actual products. for
example, timestamp router timest pro and
so on, which is amazing. And the fact
that this actually is coming from the
database, let's verify that by going
here and instead of calling virtual
scroller, let's just call it virtual
scroller one. Now, if I refresh the
page, it should be virtual scroller one,
which is exactly what we want. So, our
data is in fact coming from the
database, which is awesome. So now we
could continue building this dynamic
page further. All righty. Let's close
all the pages now and only focus on the
ID page which is the dynamic page right
now. So in here we are getting the
params ID from the params. We have the
loader data as well. So now we could
start creating a page like this one.
Again this is a card component. So we're
going to use that. Stick to that. Just
import all of them over here and get the
information such as the title, the
description, get the title, description,
right? And we also have a link here and
as well as an image. So we can pass the
link from tanstack router to / products
arrow left icon. Close the link. And
here let's import the icon from Lucid
React. And let's just say back to
products. There is an error here. There
we go. Back to products. Now this would
get a inline flex so that it doesn't go
to the new line. Item center gap 2 text
SM font medium text blue 600
700 and so on. There we go. We have this
card. Then we have the title. And now
the title is going to be text to Excel
font semi bold. There we go. See those
changes here too. And this one would be
an H1 for example. So we could move this
class name to an H1. And we also need a
span. Now we've done this before. So
let's just grab it from product card
badge. So we have this badge here. Paste
that. There we go. Badge. Now this is
going to be the card title. But this
whole thing is going to be the card
header. And now in terms of the header,
it's going to have flex item center and
gap too. Perfect. Now description is
going to be description here. And the
styling is going to be a bit bigger. So
we can say text large. And description
will also be part of the header. Then we
have the content which is going to be
the price. Card content. Import that.
And this would be the price here for
example. And card content should not be
in the header. So let's move that
outside. There we go. And now we have
two headers. We don't need that. We have
the card. We have this ent.
And then we have another card here. Yep,
that's more like it. And then the outer
card will have a Max W4XL.
MX auto E6. There we go. Then we have
the card header and back to products
would be outside and description would
be part of content. Perfect. Now it's
looking a way better way better. Won't
be justify between but definitely flex
call. We have price and we have a rated
as well. Rated section. So this will be
a span rated to be product rating and
then add reviews as well. There we go.
Learn center. And then we have at the
bottom we have this a sparkles icon. We
have product.inventory.
And that's exactly how this looks like.
Then this entire section will also be
enclosed here with product price and so
on as well. And this would have y4 for
example. This should also be a question
mark. And then in the footer we are
going to have two more buttons. A
shopping bag icon and a card footer. So
if you see this is how it looks like.
Now in terms of the footer let's do
this. This way there is no border or
anything like that. There we go. And
then in the header we're also going to
have an image as well which will be
whatever image we take. Just like that.
And let's give it flex call. There we
go. And now the badge will also come
inside the title. And both of them would
be just like that. And this would be
item start text. Let's remove this. And
let's see why this one is in the center.
All part of card component. All right.
So now if I open this, it's just like
that. But if you take a look at this
one, it's in fact a two column thingy.
So we just need to adjust that
accordingly. So now let's add a grid
around it like that's it. And we need
over here the image is on the left
otherwise not. So let's move this grid.
Move this outside the header. There we
go. Now add this. Tap that. And there we
go. It took a second. There we are.
Perfect. And now we need the same class
name year two. Perfect. So now our card
component dynamic page is looking really
nice. This needs to be item start.
Perfect. So now this is what's
happening. All righty. So now if you
take a look at the ID page, every single
product needs to have its own SEO
metadata. And the way we could send
dynamic metadata instead of panstack
start is by using the head element
and then add async here get loader data
and then not return just like that but
instead say product and now return meta
as an array and this would be very
similar to what we had. It would be
product description
and so on although we don't have all of
this. So let's just keep it and let's
add exclamation question mark in this
specific loader data is going to be
product select. So let's just say it's
going to be a product select type. Then
if I refresh the page if we go to
browser developer tools go to head we
have stack shop as the title which is
not the title for this one. It's in fact
taking the title from the root route.
And now you're going to see that we are
getting this errors saying something
about the type being wrong. So we need
to handle a case because obviously we
may not get a product data. So we can
assign this to a con product equals if
there is no product then we are going to
return a not found error and otherwise
return the product or we could throw an
error saying that product with ID not
found. Similarly, if there is no
product, then again return a empty
object for example, then remove this.
There we go. Remove the schema. And now
we're going to say throw not found. If
there is no product or that's it. So now
if we go to browser developer tools, go
to network tab, refresh the page and we
do see tanstack table premium. So it is
updating and we also see description to
be headless UI. So it is working as
well. Now we need one other property
which is that name canonical would be
let's say this URL right lash products
slash the ID itself for example would be
the canonical URL and if process n dot
node n equal to production it would be
this otherwise it would be localhost
3000/R
product slash
this should be back tech product ID. All
right. Now if we refresh the page that's
it. So now if you go to elements look
for canonical this is exactly the
canonical that means things are working
exactly how we want it. So now we've
taken a look at metadata as well as
dynamic metadata. So now just to wrap up
metadata inside of tanstack. If we go to
root.tsx tsx you could pass meta here
which would set meta tag for the entire
application. We have title description
name car set and so on. But at the same
time if you want to update things
dynamically you could have a loader. The
head tag takes the loader data as a
parameter and pass it all in as well.
See here we could also just say title
will be product name and the description
will be product description as well. So
now if I refresh the page you're going
to see tanstack table premium over here
as well. So you could update the tags
meta tags dynamically as well as
statically just like we saw which is
awesome. So in fact in nextjs if we were
to do something similar we would get we
would have the data inside the function
but we also need to call another
separate function called as generate
paradata and this is a common bug I
should say or a performance tweak that a
lot of folks can make because generate
metadata and get data function both run
twice but the fact that now there's a
loader in dansstac and that same loader
data is being passed in head means that
you don't have to call it twice. It's
just going to call it once. So, it's
already baked into the framework which
is definitely something I prefer and I
love that. Now, if we go to the
individual product page, watch what
happens. If I refresh the page, we see
this content first and then the rest of
the content. I'm going to appear the
cache and you may see like a loading
indicator over here and the content
before on top would be loaded first. Now
what essentially happens is with
tanstack start it is by per page. So we
load the data in advance and then it
provide it to the page. But what if that
some data is not ready yet. What if I
want to now show recommended products
here which is what we want to do here.
So for example I want to maybe render a
component called as recommended products
here. For example, this component
doesn't exist. So let's just go ahead
and create it. There we go. then import
right here. Now, if I go to individual
pages, you have the recommended products
here. And this is not going to be an H1.
It's probably going to be an H2. What we
would have to do is make the call inside
the loader use product loader and so on
and then pass that data. What we can
also do is get recommended products
equals await
recommended products over here. But this
is going to be a problem, right? Because
now we're going to get the recommended
product. We're going to wait for it and
then another one. We're going to wait
for it again and then pass the data and
then pass the data something like this.
We have products and this is where we
dstructure products. We don't need to
pass the SEO for recommended products
here. say data then have con data was
this right and then instead of product
would be data and when we're looping
through it it would be in fact we need
to dstructure product recommended
products and so on and then pass
recommended products as a prop get
recommended products here then add a
grid loop through all the products
products, get the product card and so
on. So now if you take a look, these are
all the different recommended products,
right? But this is a problem. You can
already see why. Now we have to wait for
not only re get product by ID, but we
also need to wait for get recommended
products. But what if we don't want to
do that? What if I don't want to wait
for the products get recommended
products? What's an option here? Well,
what we could do instead
is not awaited,
for example,
and
make it a promise that's not resolved
yet. Go here and again a promise and
then use recommended products data and
then the use function from React to
resolve the promise and this would be
recommended products data. So now what's
going to happen is this is now going to
render on the client side. And if you
don't know what use is in React for
example, this is a react API that lets
you read the value of the promise for
example. So it lets you read the value
of the resource and we could simply just
use it. So since this promise was not
resolved, we could just use use and then
get the data. Now in the meanwhile while
the data is being loaded we could wrap
it in a suspense boundary over here and
then fallback will be loading
recommended products. So now let's say
recommended products had some sort of a
delay
so that we could show the loader. Let's
say it has some
5-second delay. And if I refresh the
page, you're going to see loading
recommended products. But this data is
already ready. So it has already
returned that which is awesome. But this
the rest of the products have been
returned later. And that's just the
power of using React in general because
you can just wrap it with a suspense
boundary, resolve it using, and then use
it. So now to further customize it
because we just sort of worked through a
concept together. Let's go to
recommended products and here let's set
a my4 then this is all good and then in
in the in case of loading recommended
products we could also just have a
skeleton that we could install as well.
So we could just install the skeleton
from shad CN UI that lets you that
essentially will give you a skeleton
component outside the box that you could
show. Now I know in the actual app I
don't show this but I wanted to show you
this example that if you want you could
have a skeleton UI here. As you can see
here it's loading right? something like
this that is loading and we can just
simply load it like this. So we can show
maybe six of them while the rest of the
content is loading. And alternatively
you could also just get the div the each
two in here as well. This way the
recommended products are displayed. So
let's just add that
here.
Move that all the way to the bottom. So
now you can see recommended products but
the same recommended products is also
here. So now we have the recommended
products and then it automatically gets
displayed here. You can make the
skeleton more beautiful if you like by
adding more elements here for example
like adding a div or then adding an H4
here or I should say an H4 here for
example. You could do anything you want
to make the skeleton look good, but this
just goes to show that the recommended
products are in fact loading and but the
rest of the content is already loaded.
Now let's add create product route.
Create product lets you create a
product. You can enter all the
information you like and then create the
product which gets inserted into the
database. So let's see how that's going
to work. And this is where truly server
functions post request is going to come
in handy. In order to for us to create a
product or create a route, we are going
to say create product.tsx. And the file
is ready to go. So now if we refresh the
page, it's not working because we are
going to /create product. So let's just
go to header and let's replace it in the
nav here. Let's say slash product
/create product. Now if we go back and
click on create product. There we go.
It's working now. Perfect. So now here
we want to create a product. For that we
have a form and form has a button called
as the create product button. Now in
order for us to you implement this we
are going to use another package from
the Tanstack ecosystem called as
tanstack form. It is a headless UI for
building performant and type safe forms.
Now here's the thing. Forms can be
really complex and using a form library
is always something that I would
recommend and tanstack ecosystem makes
it really seamless for us to use it. So
all we need to do is create a use a use
form hook from tanstack react form and
bind the inputs to form state and handle
summit. That's literally it. So let's go
ahead and build it. Now if you're
wondering well why use a library? Well,
because first of all, forms are quite
complex is very hard to tackle common
challenges like it says reactive data
binding, complex validation and error
handling, accessibility and responsive
design internationalization cross
platform compatibility, and so on. And
at at the end of the day, you're going
to write the same code over and over
again, which you could simply avoid if
you were to use a library like the
Tanstack library. So let's go ahead and
set the set set it up. So let's just go
to installation, copy the npm command,
stop the server and install
tanstack/react form. Run the de dev
server. And if we go to quick start,
this literally gives us a quick start
example for how we could use it. Now to
handle validations, we are going to use
Zord, which is a TypeScript first schema
validation library. Again, it just
allows us to give a schema for the form
and panst form is just going to
reference that schema to understand okay
maybe the name should not be five more
than five characters or the description
should be two characters and so on.
Having a schema to reference against is
amazing and with zord you can define
schema you can use to validate and so
on. This is how that schema looks like.
So let's also go ahead and install zord.
Now if we see npl install zord is the
command. Now let's run the server again.
And now let's just go ahead and set up
our form. So first off let's go ahead
and define a schema. So we could say
this is going to be a product schema
which will be z dot z dot object and zed
is in fact going to be zord. This object
will have name which will be a string of
main character. But in case it's not
provided, we show an error. Similarly,
it will have description,
price, badge, image and so on.
Description is going to be one, price
will be in fact a string. And it's going
to use we going to use the refine
function which checks that it's in fact
not a nan. If is nan is false. Then for
the badge we are going to check if it
matches these values and we are al or it
could be undefined. So we are going to
see this or zen enum. Let's get the enum
or Z undefined then paid name reviews
image again it needs to be a URL and it
must be a valid URL and then we can say
the max characters are going to be 512
otherwise we're going to show an error
saying image must be 512 cares or less
and then the enum for this would be
inventory values and so on and this is
something we defined inventory enum is
what we defined here. So you can see
here these are all the enums and by
getting enum values we get the values.
Perfect. So now that we have a zord
schema defined let's use let's create a
form. So here we could say form equals
again use form and you're going to have
a
we'll have a use form which will have
default values of all of this and a
validator to be on change and this is
where we are going to validate the zord
schema. So we'll have value and then
let's assign it to result equals save
first. If it's not successful then dot
error dot issues do map will be issue
dossage return undefined and then
something will happen on submit and on
submit we are going to implement the
form submission. Now let's go ahead and
create the actual form. So this will
have a form HTML element which will have
a onsubmit handler and it will call on
sububmits. It will call E which will be
E do.prevent default E dots stoppagation
and form handle sububmit and form is in
fact use form. And now let's just import
use form from tanstack form. Let's
remove these validators. Everything has
been fixed. All the type errors are
gone. And now in terms of the form
itself, we need form dot field name the
field and then label. We don't have
that. But we need to install that from
SHAT CN UI. So we need label.
We need input. We need text area CN UI.
And we also need a field error component
as well from SHN UI. Or I guess not. All
right. So now let's get label from shad
cnuy input from shad cnuy and let's say
this is going to be space y2 otherwise
it's going to be a feed error and
because we going to be copy pasting it
everywhere we we can create it we we can
create a common component called as feed
error. So this is how that component
will look like. You can just grab that
from chats. You can grab that from the
cheat sheet or the GitHub repo field
error something like this wherein it
will be a destructive text with the
error message where we are parsing the
single error and so on. We don't need to
import react here. And let's get field
error and import that and pass that in.
So it will be dot error field dotstate
dot meta dot errors. And now form field
is name and this would just be field.
And now we would create name. We would
have value. We would have an onchange
handler which would be field handle
change e.argate.value.
We'll have a placeholder. We'll have a
accessibility tag for field.state dot
meta dot is valid and so on. Perfect. So
now if we go to go here you're going to
see this form created which is awesome.
But let's give it some sort of a layout.
This way we it is easy for us to see the
changes. So, max w7xo
py8 px4. All right. Now, uh let's add a
card component with a card title that
would just have a create product as you
can see. And then the description would
be fill in the details to the catalog.
In the details of the catalog. Now, for
the title, the class name is going to be
text large. And for description, it's
going to have a line column two. So much
better.
Card header gap is going to be two. Now
for the form as well, it's going to have
a card component which will wrap the
entire form. And it's going to have a
card content as well. And this card
content will wrap the entire form too.
There we go. And both of these will have
a div around them which will have a
space y 6. All right. So our form is
starting to look a lot better. As you
can see we have product name and so on.
This would be product name. Now let's do
the same for all the other fields. We'll
have a name of description. This would
be description. Let's remove that.
Instead of input, instead of input, it
would be text area. Let's remove the
type. And on change placeholder would be
enter product description. And this
would be information exclamation. There
we go. Now for the form, it will have
space by sake so that there there's some
room between elements. Next, we will
have a form dot field name will be
price.
Then it would be a field again which
would have the same thing as before.
Let's use input here. There we go. And
this would be price laceholder
be 0.0.
Depth will be 0.01.
Type will be a number and everything
else stays the same. Then we will have
an image. This would be image. URL.
Placeholder would be https.
example.com/image.jpg
and the type would be URL. And now the
input we're using is directly from react
input. We need to use / UI/input.
And now you can see that borders and all
that which is what the shad scen input
is. All righty. So now we have the
image. Now let's create the badge. Let
it be badge. Let's copy this. paste it.
This would be batch optional but instead
of input now this would be a select we
don't have that so let's add that from
chat cni
all right so now let's say value equals
field dot states dot value or empty on
value change equals value field dot
handle change value as patch value and
get the select component from UI select.
Now we need a select trigger which will
be id dot name class name would be w
full. This trigger would be basically
select value and then it will have
select content. This is just to display
all the badge. So now we'll have select
item value equals empty none. Then we'll
have new. This would be new. Then we'll
have sale. This would be sale. We'll
have featured. This would be featured.
We will have limited. And this would be
limited. There we go. Now this would be
a select. Select would be from UI
component. Select. Now if we take a look
at badge, let's just instead of getting
it from enum values, let's just say new,
it would be an enum actually. Union Z do
enum. So this would be new sale featured
and limited like that and then Z dot
undefined. There we go. So now for the
badge is this one. And then let's also
add this as well as imported. Add a
badges undefined over here and cast it
as well as this as a constant. Now the
next step is inventory which is also a
select. So we could just copy this paste
it. Now we will we're going to have
inventory.
This would be
status
double
not be empty
or handle change value would be asked
value and same thing here it would be in
stock back order preorder and then as
you can see here we're getting an error
so instead of casting it as a constant
And let's just say inventory value.
There we go. And now this would be in
stock. The value of that would be in
stock. This would be back order. This
would be pre-order.
And let's remove these two items. This
would be back order. And this would be
pre-order. Then the page. And now you're
going to see that it works as expected.
And lastly, the last one is form
do.Subscribe which will have a selector
and this will take the state and it
would be an array and say state dot cans
sububmit comma state dot is submitting
for example. Close this here and now
open cansubmit/ma
is submitting and then do this here.
Let's add a div for flex gap 4 and here
there will be a button type be submit
and then let's import button as you can
see and type submit disabled will be not
can submit or is submitting is when we
want to disable the class and then the
class name would be flex one here we can
say is submitting will be creating or
create product and then button type will
be button will be cancel then variant
would be outline on click would be
navigate
is a function from router which I'll
show you how it works but this is what
we are going to add slash products
navigate is doesn't exist yet, but we we
can create it by calling navigate equals
use navigate from tile stack router.
This is very handy if you want to
navigate to a dynamic route or just a
static route and so on. There we go. So
now you can see that when you try to
open up the select it is not working.
Select is not working at all. Why is
that? And if we add a submit button and
we have a onsubmit handler here. Let's
add a console.log
submitting. Now if I open up browser
console close this submit I don't get
submitting. In fact it's refreshing the
page even though I'm adding e.prevent
default. So it's not respecting any of
the events. That means this is a common
Tanstack issue and I hope they add
better developer tooling for this in the
future. But if none of the events or
like buttons or event handlers or select
events and so on, if they stop working,
that's because something has gone
terribly long in your application and
all the interactions have essentially
stopped working. Now these are all just
links, right? So it's going to work.
This is data so it's going to work.
However, when it comes to take hand
eaters, all the interactivity has just
disappeared. Why is that? Well, let's
take a look. So, uh the reason is
because if you go to index.dsx here, we
call get all products.
And since this specific server function
runs in a loader and a loader runs on
the client side as well as the server
side and get all products, it's actually
a DB related function, right? So it's
this specific function is not going to
run on the client at all. So in order to
fix this we need to get all products
from this command
from this pack function called as import
which imports dynamically. So products
dot and then we await get all products
like we do and then return the data and
remove this import from here completely.
This is one issue for sure. So now if we
go to create product, it's still not
working. That's because we probably have
done something similar somewhere else
too. So now if we go to index.tsx
in product, we didn't do this, right? If
we go to index.tsx here, we need to do
the same. Get recommended products and
remove this import from here. For
example, then we go to ID. Have we done
something similar here? We're calling
get recommended products. Let's get
product by ID. Let's see if I refresh
the page. Create products. Still not
working. So, let's do the same here too.
Get recommended products. Remove the
import. Get product ID. Let's remove
this import too. And now you can see
that our interactions are back. It's
working now. And you have that's one
thing you have to keep in mind with
loaders. They run on the client as well
as server. So if you are running
database calls you have to be careful.
Now continuing on since our handlers are
working for example the minute we enter
this information and press create
product we should be able to create a
product for example. So for that we are
going to create a post request over
here. Here we're going to say create
server function. Create product server
function equals create server function
method would be post and then dot we
going you call another function called
input validator where data will be
create product data and we creating it
in the wrong file. It should be in fact
here. So this would be something like
this. And let's move this to the bottom
so we can reference it. Server function
create product data will be data. And
this type would be create product data
equals name would be string. Description
would be string. R would be string.
Image would be string. Badge is optional
which would be
sale or featured or limited.
Inventory would be in stock or back
order or reorder for example. And then
we'll have dot handler async
data should be promise do select. If we
go to schema, it would be yeah product
select and this is where we would create
the function from DB schema. Now same
idea con
create product equals await import at
data/ products and then return create
product
and then the data. Now create product
does not exist but let's just go ahead
and create it. Here let's create a const
create product server function equals
create and here we would have export
async function create product
data would be product insert returns a
promise of product select and again a
try catch block error creating product
and then this would be response result
equals await db.insert
products data dot values. Whoops. This
should be products. Okay, that's a
schema. This is where it would be data
dot returning and then return result
zero. There we go. This would be return.
And let's remove this. All right. So now
if we go back to our route, we need to
map the product data. So we can say
product data. Product insert equals name
would be data.name. Description would be
data description. Price would be data
dot price. Image would be data image.
Badge would be data dot badge. Null.
Inventory would be data dot inventory.
and so on. And this time it would be
product data. And then you're going to
add this condition that if there is no
product just return this error. For
example, we going to throw the error.
All righty. Now we need to invoke this
onsubmit.
So if you take a look the button has
onsubmit, right? This is not just here
but for the entire form it is right
here. So here we need to add a try catch
block console.
Error creating product error. Then the
try catch block would be awaits. And the
server function we just created should
be this. And then pass the data which
would be name would be value.name.
Description
would be value dot description. Price
would be value.
Image would be value dot image. Badge
would be value dotbatch.
And inventory would be value dot
inventory. Now here's the thing. We
could also simply just get name,
description,
price,
image, badge, and inventory
from here as well. And then change this.
Somehow it doesn't like that. So let's
keep it as is. All right. So now once
the product has been created, let's see
what happens. So now let's just create a
product. Call that dance stack hats.
Cool hats. $89.
Let's use some hats from Unsplash. Let's
use this. Open image in a new tab. Copy
it. Paste it. Bad should be new. It's in
stock. And create the product. There we
go. So now we're now going to go to
superbase and you can see there are two
tanstack hats that got created which is
awesome. That means our query is
working. But we need to do one more
thing. We need to navigate the user as
well. So here we can say navigate
let's say two / products. So now if I
say can stack another hat full hats 78
this sale back order pre-order and there
we go we get another hat over here which
is awesome. But now if we just want to
make sure that we are invalidating all
the route loaders and we want to wait
for completion before navigating, we
could also just run router dot
invalidate which and then sync true and
router in fact comes from tanstack
router as well. So here we could say
router equals
use router from tanstack. So we could
invalidate that too and then navigate
the user just to make sure. All righty.
So our form has been successfully
created just to make sure it's working
or not. And it is. Tanstack hat is
working. But another one did not appear
yet. Another hat did not appear. So if
we go back here and refresh the page, we
don't see another tanstack hat. In fact,
do we see another hat here? No, we
don't.
So let's see what happens. So if we go
to create product, let's just say Anki
and Hank 34 product sale back order
create product. And now we do see it. So
it was probably just a minor glitch, but
it is working. Our app is working really
well. So see, watch what happens if I
refresh the page. I'm on the homepage
and we get all the data. I go to
products and go back to home. It doesn't
work. Why is that? Why does this break?
Well, that's because our loaders
runs in two places. Server side and this
with serverside rendering in a NodeJS
environment and when you navigate
between notes in a client environment.
When we use an import like this, the
bundler includes all the dependencies in
the client bundler as well. NodeJS
modules such as PG doesn't exist in the
browser. This causes errors like class
extends value undefined because NodeJS
tries code tries to run that in the
browser. So the best way to fix this is
to separate serverside code and client
side code and for that we need to create
a like we did in create product. So here
you could say patch products function
equals create server function method is
get and same idea I get recommended
products here and now return the fetch
products or it's no longer in in object
and that's it. So now if I go to
products it's fine. If I go back to home
it also works. Products home also works.
And this is the most ideal way to do it,
especially since loader is running on
the server side as well as on the client
side during navigation. Now it's time
for us to build the add toart
functionality. We won't be building this
page from scratch. It's pretty much the
same thing. We have a card component, a
bunch of styling, a few buttons here, a
checkout button that doesn't do
anything, and this increments the count.
you want to stick to the main meat of
the application which is related to tan
stock start. So I'm going to be
providing you the structure for the
specific page. It's now time for us to
work on the cart page. This specific
cart page allows you to add to cart view
the items that have been added to cart
and so on. So for example if we do stack
tan stack table premium then you're
going to see that this is another
product. I can increment the product. I
can increment it, decrement it and so
on. And that according me updates the
total as well, the shipping price, the
subtotal and so on. The meat of this
application, this page is in fact in the
tan stack part, not necessarily in the
layout. So I'll be providing you the
layout for the specific card page. So
let's go ahead and build it. So first
off, let's close everything and let's
create a card page. For that we are
going to create a new folder or rather
you can just create a new file called as
card.tsx. Now this card.tsx is simply
going to go to hello cart now cuz this
file was created. Now I'm just going to
paste the entire content of the page and
hit save. Now empty component doesn't
exist. We are going to install that from
chat CN UI. So we can call this empty
component is essentially a empty shell
UI that dance that shaden provides us.
So now let's refresh the page and there
we go. So this is how it works. If card
do items.length is zero then we simply
show this card component with empty
title, empty description, empty content
and so on. Then we set up the layout of
the page with H1 tag and a button
appearing the card. And then we have all
the other information and this could be
replaced with a input button for example
if you like or just keep it as input to
make it keep things simple. So if I
browse products we go to browse products
but if we go to cart then the cart is
currently empty. So let's implement the
cart functionality. So now let's focus
on caching which is we're going to use
tanstack query. Tanstack also has
built-in S swi caching that we'll take a
look at and there's also a long-term
in-memory caching for route loaders as
well. So first of all let's take a look
at how we would implement cart and as
part of that we'll go over the caching
principles as well. In the add to cart
the specific count increments. If I add
to cart again it increments again. If I
go to page add to cart got what
implemented. If I go to cart it has
these three different products four
products for tanstack cloud pro one of
this and one of tanstack query plus it
also calculates a subtotal. So this
specific cart we definitely need to
store it in the database in the carts
table just like that over here. As you
can see we have added one specific cart
item. So what we could do is go ahead
and fetch that. So we could display that
and see how it looks like and then start
implementing the individual mutations.
For example, when I press, it should add
one item to the card. When I press
minus, it should delete the item if
that's the only item. So the logic here
can be really complex. At the same time,
if I say remove, then it should also
remove the item, not only from our cart,
but also from our database. And if you
haven't already, make sure to just
create pick a product ID that you like,
add that inside the cart items table.
All right. So now what we are going to
do? All right. So now let's implementing
let's implement getting the cart first
of all. So we are going to use route
loaders. We love loaders, right? Because
they allow they are isomeorphic
functions that allow us to get the cart
information. And this is where we can
call fetch cart items or something like
that, right? And here we could call
fetch cart items. Create server
function. And let's create a server
function. And this one we'll call get
cart items function. Now remember we've
experienced this before. So we are going
to get card items. We are going to
import it inside a add a import and this
specific import command is something and
we just need to pass that in just like
this. All righty. So now we have we
going to dstructure get card items
function and remove the import from
here. Now here's the thing. Get art card
items fn does not exist yet. We're going
to create it. So let's head over to
data. Let's call this cart.ts. And in
here, let's call the get cart items
function. Import the DB. We need the
cart item schema. And then we also need
to inner join with products because when
we get the cart, if you think about it,
we not only get the cart information, we
also get the product name, then price,
whether it's in stock or not, the
amount, and so on. So we need to get it
for example but for now let's just keep
it simple and let's return the card now
now as we do this specific card that
exists now for example and we have a get
card items function. So let's copy this
and so let's get the card and here have
it. So we're going to await it and then
cons data
would be await cards item and just
return the data. All right. So now if
you take a look, we can simply use the
route loader like we usually do. And
let's call use loader data here. And
this specific page expects it to have an
items array which right now our specific
get function doesn't really do that. It
has card do items and the length. So
first of all, let's comment this out.
And now let's log the cart. Go here.
You're going to see that items does not
exist. That's why it's complaining. But
if you go to the console, you're going
to see a cart information. Our cart
information is over here, which has an
array of one item because we only have
one cart. All right. So now let's
transform the data. So let's go back to
our cart. And over here, we could simply
return card just like this. And now you
can see that there is some card
information. It shows $8 which is the
wrong value but at least there is some
cart information here. So we need to
transform the data. Now the next step is
to also get the rest of the data by
creating an inner joy because this is
the information that we have from the
cart that there is at cart but all the
other information comes from the
products. So let's get the products get
get eq and let's
equals c items product ID equals
products ID. That's the inner join we
want to create. And if you're not
familiar with inner join, it allows us
to basically combine rows from two
tables that have matching values. So in
our case because cart items has for
example product ID and this is the ID of
the product and that's what we're using
for referencing it. Right? So that's
exactly what we could use here. We could
use the inner join to combine a bunch
combine rows from two table. Now if I
refresh the page still nothing's going
to happen. So let's take a look. Now we
have card do items.length but we also
need to just spread the rest of the card
as well. So here we could simply do
card items do map cart dot map and then
spread the products because if we
console log cart on server side for
example if we look here on the server we
get card items as an object and we get
products as an object. So we could just
simply spread products and and map it
exactly to how we would expect. So as
you can see here we need the quantity
from card items but product ID already
exists in the other one. Okay. So we
have mapped that now. So now if we
refresh the page, scroll all the way to
the bottom. We're not referencing it. So
let's just do cart do items do map. And
there we go. We do get we do get
everything we want. For example, we have
the image. We have the title whether
it's in stock or not and so on. So we
have rest of the information here just
like we do here as well. Regardable.
Beautiful. And now this specific item
would be of cart item select and a
quantity of number. Now cart item select
is in fact card items.info select and
quantity although it actually is cart
items infer select and I would say type
of products.info select as well.
Example. So, let's save this and again
revoke this. And now this works. Our
type has been fixed. Right now, if it's
not really detecting the cart that we
just added yet, but don't worry, it's
just the editor being slow. Okay. So,
now if you see, we have a cart and we we
we can't increment or decrement the
count. However, we could update the
subtotal and the total here. So sub
total right now is zero. But we could
easily do the math here for the subtotal
for example. And now it's doing the math
too where subtotal is 178. And then
total becomes subtotal plus shipping.
Awesome. This comment now and our cart
is pretty much good to go. Now here's
the thing. When you increment it, you
want to add an item to cart. So
decrement it. You want to remove an item
from cart. Basically update the cart
item quantity. If we click on clear
cart, we just want to remove the cart
entirely. Or if you press remove here,
we want to remove this specific item and
as an entire set from the cart. So we
need a bunch of different mutations and
for that we are going to use a create
server function method post as we've
done so in the past. So first of all we
have the get cart item. So let's remove
the console log here. And right here,
let's add another function. And let's
call this mutate cart function. Equals
server function. Get it from tanstack
start. Call the handler. And really, we
don't need a request. We just need data.
And this specific data does not exist.
But we're also going to call another
function here called input validator.
This is going to validate that we are
going to need a few things such as the
product ID and the quantity. Then we
need this. Let's move the dots to the
next line. Let's close this. So this is
what we this is a server function that
we are going to need. Here's what we
need to do as well because we could use
the same server function and depending
on what whether we are adding an item,
updating an item or removing an item. We
could take additional parameter called
as action which could be add, remove,
update or clear. For instance, what
would what this would do is if a action
is add, we add to cart. If the action is
remove, we update we remove from cart.
If the action is update, we update item.
Or if the action is clear, we clear
card. So that becomes pretty
straightforward for us to have it in one
function. So first of all, we going to
need a switch statement here says data.
And we can confidently call this the
fact that we have this input validator.
if because tansax start does a really
great job at validating the inputs and
only pass through the handler if those
specific inputs match. So for example
here we could say case add and then
something like this would be the code.
Now but again let's not get ahead of
ourselves and let's remove all of this
for now. So now let's add the add toart
function. Now this specific add toart
function is actually not a tan stack
function. So let's remove it. And in
this one we just are going to need going
to be a async function add to cart which
would take a product ID take a quantity
and if a quantity is not provided it
would be one by default and then first
of all here we need to check if an item
already exists in the card because if an
item exists is only when we want to
increment it. The max number of
quantities we allow is in fact 99. So we
could say math domax quantity 1 math
domin quantity 99 something like this.
So then we need to check if this
specific item exists. If it exists we
need to update quantity. But if not we
need to insert a new item for example.
And that's our logic here. To be checked
if an item exists, we could simply call
use this specific query wherein it's
going to select from cart items. Check
if the product ID matches and limit it
by one. If existing item length is
greater than zero, that means it did
find the cart. We need to update the
quantity. update functionality is not
the query has not been written yet but
we can write it if and this is where we
do else it's going to insert the item
for instance. So now let's update it. So
similarly you can say new quantity will
be math domax. This time instead of
quantity it would be from existing item
do quantity plus qdy because whatever
the existing quantity is let's say there
are three items and we adding one. So it
would be one and so on. And next let's
update the item. So we can say db
do.update update card items dot set
quantity so on. So now we are going to
get they're checking if card items set
ID is equal to in fact we should not do
card items should do card items product
ID cuz that's our reference there. We're
referring product ID. We're connecting
the tables with product ID. So this m
this means it's going to be added. Else
let's just add a brand new item. It's
going to insert cart items dot values
product ID and quantity as quantity for
example new. So now our add to cart
functionality is looking really good.
And if nothing works if there is no
existing item we are turning it for
example and for some reason we end up in
this weird case where you could just
call get cart items function over here.
Okay. There we go. So now our cart is
looking really good and we already
created the update cart right because we
already updated the quantity. So we
could just extract that out. So we could
say get update card item check if an
existing card exists and then only
update it. All right. So now instead of
calling this here we could just await
update card item. product ID and
quantity would be existing item plus
quantity for example because your update
card item only takes quantity doesn't
care how many so we're going to do the
math for it and call quantity there
perfect now here we could update the
card too and update card item there we
go now we have two specific actions
needed add and update so now let's hook
it up I'm going to go back to the card.
Let's add the increment count option.
So, we have this plus icon and we have
the minus icon. We have plus. So, let's
add muted card + one. Let's call m let's
call muted card function here. Let's
await it. Let's make this async. And
then similarly, this is the input. for
the minus one we want to do the same but
update it to item quantity minus one for
example so now let's see if that works
so if if we refresh the page it says
class extends value of I'm defined is
not a constructor should be cart and if
we take a look at the cart we are
exporting it and yet again we get the
same error classic and that's why it's
so important that the way we structure
our code is very good especially
especially in nan stack start the fact
that this is a function again we're
calling the database and let's extract
this out remove this and move this right
here server function and now let's
import add to cart and update cart here
from cart awaits both of these for
example so if you take a look we need to
export the async function and export
add to card here and remove server
function from here. So all our
serverside code will live here. In fact,
I would also say let's rename
card.server.ts.
This way it has a proper naming
convention as well. Now we know it's a
server file that you're importing here.
But this is all serverside code. It
accesses the DB and so on. All right. So
now if we let's see if we hooked it up
properly. Let's decrement the count from
here. Let's increment the count. And now
here we just need to add data because
that's exactly how we have set up our
input validator. Same thing here. Data
as well.
There we go. So now let me refresh it
one more time. If I increment it, it
should increment. If I refresh it again,
it's five. If I increment it, refresh
it, it's 11. So there's something wrong
with our logic because if I increment it
will actually be 23 which is not what we
want. So it's not incrementing it right.
So let's just use this to be a number
also make this a number. Right now if I
incremented it's 47. So if we go to new
date card function go to card.server.
And now if we go back to cart let's see
how we increment it. If we say
decrement, I refresh the page. It works.
That means there's something wrong here.
So, we just need to do + one here as an
increment. Now, if we refresh the page,
if I increment it, refresh again, it's
95. So, that's working really well. But
notice that this specific value is not
updating at all. We have to do a hard
refresh. And for that, we could use
router.invalidate.
And for that we could use awaits router
dot invalidate sync is true. Now if we
go all the way back here in the router
we need to import router from use router
which comes from alstack router for
example and obviously there is and we
are closing it over here. So let's just
open this up
like that. Let's refresh the page. If I
increment it, it increments.
It increments. And the router dash has
been invalidated. So if I decrement it,
it does not decrement. So let's do the
same here too for decrementing. Router
invalid. So now let's refresh it one
more time. Let's decremented. It works.
So this is beautiful. And incrementing
works as well because the router also
tries to cache as much as possible. And
this specific pattern is more common
than you think. All righty. So now we
have implemented incrementing as well as
decrementing. So now what we could do is
go to the rest of the application and if
a user presses add toart, it should also
work. So we could simply just copy this.
Actually add toart would be this one. So
let's copy this and then go to product
card which is where we have it right
here. Let's all of this and let's make
make this async import mutate card
function product ID is product ID we
already have it product ID and then for
the router we just need to create the
router here that imported from tstack
start all right so now we have all of
this if you take a look now refresh the
page if I add to cart and adding
tanstack router Let's add something
else. Let's refresh the page here. We're
not getting the cart count from the cart
right now, but let's add tanstack query
enterprise. Go to cart and tanstack
query enterprise has actually been
added, which is amazing. So now if we go
to the database, go to cart. We have now
three different carts. We have three
different cart items. We have quantity,
we have product ID, and we have created.
All righty. So this is working really
well. But this specific count does not
update. So for that if you go to header,
we need to get the count as well from
here. So we could simply write a
mutation here that gets that.
>> All right. So let's go to the server. We
get all the cart items here, but all we
need is the cart count. So we could
write a simple query cart get cart count
card items.
and it just returns the count. It
returns the number of items in the cart.
Here we could simply call get cart and
then do something like this that returns
the item number of items. Let's remove
this count as a number and so on. All
right. So now if we call it to call a
server function that gives us get card
items. So let's say get cart items
function update function and something
like this which imports it from the
server. You invoke it and now this needs
to be an internal thing. So let's remove
this also remove this as well. Now let's
just do get card idle something like
that. All right. So how exactly can we
call this specific server function that
we just created? because at this point
we don't have a loader that we could
just do use loader data or this is also
a component that will run on the client
side. So how do we do it in the easiest
way? Well, we could simply just use
react query that returns gets calls get
card items count. Then card items count
is what we could show over here. For
example, something like this. We take a
look at the server. you get the item but
we said want to get
count and then we have get part items
simply the count here and let's see
where we are not calling it we have some
we have renamed the function so let's
just get fix this import this fix this
and so on all righty we reserving the fn
to be for server function all righty so
now if we go back here. Yeah, let's
return items dot length.
Go. If it does not exist, it will be
zero. Perfect. So now it's three. We
have one, two, three products, which
makes sense. We have three items in the
cart. And then we also show the value
price as well. So you can see we could
actually do this entire thing inside
cart.
Not having to do it repeatedly here. So
we could return count plus total.
Perfect. And now we could return call
this card items data. This would be dot
count and this would be total. Perfect.
This will be card items data. So if you
refresh it, it's zero now because we're
calling the wrong function here. There
we go. So now if we refresh it, you're
going to see 95 94 95 which makes a lot
more sense rather than counting the
number of items. The number of items
should be 93, 94, and 95. Perfect. And
this is the total value which is 8534.
This shows 8526 because we're not
including shipping. All righty. So now
if we go here, user will see the
shipping value and then they will get a
better understanding. Perfect. So now if
we go back to home, refresh the page one
more time. Let's go to products now and
let's increment. Anki implemented. Now
after incrementing it we went here item
got added. I had to refresh it to update
the count. So we could try to invalidate
the cache as well after we add to cart.
So if you go to product card, we also
need to invalidate the specific query
client cache as well. Let's get query
client and we have query and let's not
let's go back to home. Increment it.
Refresh the page one more time. Now it
should be 97. Let's increment it. And
now it's 98. Perfect. So this
invalidating queries is working because
we gave it the specific query key. All
righty. So now if we go to the actual
card and incremented when I see that
it's not updating the card count because
card count is stored in the react query
cache. There is a cache for the router
as well. So there are two different
types of cache. Take a look at that in a
second.
But we also now need to invalidate every
time we increment or decrement the count
as well. So let's head over to cart and
we need to invalidate this and we need
to invalidate here as well. Now let's
use add a hook for use query client. And
now if I increment it you're going to
see the value right like so on. All
right. So it's working really well. We
also need to add some float around it or
math dot something cuz the total is in
fact looking crazy. There we go. Then
increment it 106 107 and so on. Perfect.
All right. So it's working really well.
Now if you also notice the minute I
increment suddenly the order changes.
Now did it work or not? So it's not
correct. So let's go back to our cart or
cart.server. And when we get the card
items, let's order by based on created
at and use a function called descending.
Now if I refresh it, it's always going
to be the same in place because we're
incrementing. And if I increment this,
it's also incrementing it properly cuz
we have added this border by value. All
righty. So our cart is looking pretty
good. We are able to add to cart. We are
able to go here decrement the value as
well and it's working really well. What
if I click one more time? You're going
to see that again it does not remove
this item. This item does not exist yet.
So we need to handle that specific
logic. So now if we go to card.server
here
and we update the count for example, we
need to check here if quantity is zero.
Let's just delete it because we no
longer need it. So now if I refresh the
page, we need to delete the item.
Decrement it. Let's also set this to
zero. Refresh it. Decrement it. And it's
gone. Because if the quantity is zero,
then we delete it. And why even run the
rest of the code if it's not going to
work. So we could simply just add a else
condition there. So now if I reduce the
quantity here, boom, got deleted. Now if
I refresh the page, it still works as
expected. Beautiful. So now our update
card item is looking a lot more
reliable. Now if I press remove, nothing
happens. So we could hook that up. And
we already have this specific code here,
right? So we could go back to our cart
mutation. And this is where we could
work on implementing return awaits
remove cart and return await clear card.
These two don't exist but let's get them
from here. Go to cart.server and
implement it. Now we could say remove
cart based on the product ID. Add a and
here we are also going to implement
clear cart. It's going to be a delete
where
fart items do quantity greater than and
these are all handy utilities. Test that
the first expression is greater than the
second expression. So if it's zero and
zero we could warp. All right. Now we
just need to hook it up. So if we go
back to card page, we have the we have
clear functionality here. It clears the
entire card. Honestly, then
shouldn't be a product ID here. If we go
here and we to add it conditionally. So
we need to say if type is here, product
ID is not required that. So let's create
a type input and then let's only make it
optional. So it would either be this or
it's going to be
for clear it's it's not going to have a
product ID so it will be optional and so
on. So let's remove clear from here. All
right. And let's remove this comment as
well. And now we're going to take a new
date card input as a type here. Remove
this all the way to the top. Typically,
I would love to extract it as a separate
type, but in this case, it's only one
type. So, I'm going to keep it there.
All right. So, now we can remove this.
And now, it's no longer going to throw
an error for clear. So, now if I say
clear cart, cart is gone. Deleted. In
fact, if we go to stack shop, the cart
items are gone completely. All right.
So, this is working well. Now, let's go
back to home. Add to cart. Go to cart.
And now, let's add another item also.
add one more item here. So now let's
remove tanstack query enterprise. So if
you press remove, what happens? Well, we
haven't hooked that up yet. So just need
to do remove
this code. I think like this. This would
be remove. And if we go take a look at
our card.server or cart, we have a
mutation. We have remove here which
removes from the cart which removes from
cart completely. And if we take a look,
this sets it to zero. All righty. So now
we go to card.tsx and
remove it. Let's find remove. And now if
I remove it, nothing really happens. So
if we go back here, remove card, we are
going to remove that item completely. So
we don't even need to check for quantity
and all that. We just remove it
completely.
Fact that it exists that means product
ID is mapping. We just need to remove
it. Say if I remove it works. Remove
card is empty and gone. All right. So
our card functionality is also working
out really well. Let's remove this
unused import. So we are able to add to
card, create a product, update the card
count, refresh the page and so on.
There's a lot that we have done here.
Now next step is to refactor our code a
little bit. So now obviously this is an
empty cart state. So let's create a new
folder here called as cart and let's
call this empty part state.tsx
and render this whole thing here. Let's
move it and return empty card state.
Empty card state comes from here. We
don't need double returns. There we go.
Now let's copy this whole thing. Move it
inside of here. For example, get the two
icons. And I always like to get the icon
version of Lucid React cuz Lucid React
gives you two versions. The icon with
the without the name icon and just the
icon itself and icon makes it more
obvious. All right. So let's remove this
as well. So this is this is already
looking better. Let's remove this
console log. So now this these card
items entirely could also be mapped
differently or we could keep it as it
is. We also have this summary row that
easily be also extracted out as well
function.
There we go. And summary row can be
imported as well. Perfect. Now let's go
back to cart the route cart. I believe
we have the same logic everywhere. If
you see in stock about here, but Dor is
in stock, vector is in stock. I think we
should be good there. Then we have these
different buttons and they're inline
handlers. So we could simply just say
handle update quantity, handle remove
item, handle clear card. Let's do it one
at a time. Let's remove all of this.
Let's create one for clear card. So this
would be handle clear card and then we
going to call clear card here.
Similarly, we have we have this entire
function which would be handle update
quantity or handle
decrement quantity
would just be this whole thing
pass the item start item select this
would be the item that we go here
would be this. Similarly
also get this whole thing out which is
handle increment quantity add it here
add the item as well we're going to fix
the types and lastly it the
remove it's going to be handle remove
and again
>> handle remove. So if you take a look, I
think you have typed it incorrectly.
This should be product select quantity.
Need to import that. Same thing here too
as well as here as well. E product
select. To remove this unused import as
well. So now we could just extract the
type to be card item will be product
select. Here it would be card item, card
item, card item and so on. Now it's
looking so much better and well
contained. You refresh the page. You're
going to see browse products.
We could also extract this out into a
card footer as well. And let's call this
card footer.tsx.
And let's import this here. Let's add
the summary row with all the types.
Let's add the button from UI button and
so on. All right. And let's add the
necessary props and import it.
Beautiful. Let's remove summary row from
here. And lastly, let's extract these
two types now. So now there's no longer
one type. There are multiple types. So
let's create a types folder. And here
let's say card types.ts.
Add these two items. Export it. Same
thing. Export it as well. And then let's
import these two. remove product select
from here. Import cart item as well.
Beautiful. All right. So, our code is
already looking a lot better. Let's
remove unused imports here, too. And
let's add error handling here. That's
it. It's looking pretty good. If I add
to cart, increments the item. If I add
the cart again, it increments the item.
But it takes a second. Then if we go to
products, add to cart, add to cart, add
to cart, it's working seamlessly. So if
we go back to home, let's see what's
going on. So if we go to routes and
index.tsx, we have get recommended
products. And I believe inside of data,
we adding a delay. Let's remove that. We
no longer need it. We don't need that
delay. And we don't need to await it
either because we're not awaiting here.
Say that's good products page. So now by
add to cart instant because that delay
was causing a problem. Perfect. Now we
have successfully removed a delay. If we
go to products add to cart it works. Go
to create product. We are also able to
create a product. So let me can say an
stag beach beach bag
beach 45 some image limited edition it's
in back order and let's create the
product and now you're going to see tag
beachback here if we click on it click
on it it also works all righty so now
it's time for us to deploy our
application because it's ready to go so
first off we are going to use appite for
today's deployment. This is not
sponsored, but I love that they are an
open-source cloud plat development
platform for developers who like to get
things done. So, they are essentially a
versell alternative and they are gaining
quite a bit of popularity. They have
functions, o databases, all that too.
But we are going to mainly be using it
to deploy tanstack start. So, first
let's click on start building for free.
Since I already have a product, I was
did not sign up, but please feel free to
go ahead and sign up. Next, let's create
a new project here. And let's call this
start shop app. Now, pick a region
closest to you. For me, it's Frankfurt.
And let's create. Now, there are a few
options here. Let's press web. And this
time, we're going to say Tanstack start.
Let's create the platform. And now, it's
going to give us this these bunch of
these values. It's asking us to add
these values to our env file. So, let's
copy it. Go to flo.env file and paste
it. There we go. Now, install the
project dependencies. Again, it's like
trying to get us to install a bunch of
things. We don't need it. We already
have a project, for example. It also has
a setup prompt, so it will do all of
that for us if you're using AI, right?
And now, let's open up. Now, it's trying
to wait for the connection. And I
believe we need to install the SDK as
well. So let's just copy the setup
prompt. Press this. So it's going to
enter and it's going to ask it to
separate basically install appite. So
I'm going to install appite. If you
don't have AI, then make sure to just
npm install app right here. And then
it's creating a app.ts file. And then
inside the root is adding client. There
we go. So now we're going to keep all
the files that we've just installed. So
if we go back here, it says,
"Congratulations, you have connected
your app successfully." So now we could
open up local host 3000. It worked.
Let's skip and go to dashboard. Now we
could have used their database, but we
already have Superbase, so we don't need
to worry about it. We don't have O here,
but we could set it up. Although I
really love Clerk as my o provider. All
righty. So now let's go to overview
tanstack and you can see that there's
option for creating a first site. That's
exactly what we need to do. We need to
create a site. Connect to a GitHub
repository. Connect to GitHub. Now I'm
going to use my GitHub. I verified it.
I'm going to give it access to all the
repositories. For instance, give me a
sec. And now these are all the different
projects that it has pulled from
apprite. All right, let's close this now
and create our GitHub repo. So, first of
all, I was committing all the changes as
we go along. So, add apprite and fix
bugs is what we want to do. We don't
have a GitHub repo yet. So, let's go
ahead and create it. Let's go to
github.com
repositories. Create an repository. I'm
the owner. And here let's say we want
stack shop app. I'm going to choose
public for now and then create the
repository. Let's copy this. Head over
to the terminal. Stop the server. And
now we have all the changes. So let's
just paste what we copied. And now it's
just going to add the remote origin and
ship our app. If I refresh the page now,
we see all our changes here. So now I'm
just going to go next and previous for
it to refresh the GitHub list. And there
we go. We can press connect now. And
let's get all the environment variables
that we need. So let's go here and go to
env file. Copy it. Go here and paste it.
Well, they don't have the same option,
but you can just do so here. Env. Now
let's save it and let's go ahead and
deploy it. So now it always gives us
this domain and we're going to deploy it
to stack shop app. I believe the other
one was prod. So let's just use stack
shop in that exist. It does. Now let's
just go ahead and deploy our
application. So now this is going to
essentially install the dependencies as
well as the dev dependencies and
basically run npm run build. It's which
is going to kick off the build. So if we
go to backend JSON, it's going to
basically run v build here, which will
create a production build. And if
everything goes as expected, then it's
going to deploy our application.
However, we don't know if it's going to
because we did not run the build locally
yet. So if it fails, we'll run the build
locally and go from there. So now it's
going to simply create the environment
so on. All right. So let's take a look
at this build now. So you can already
see that it's running the v build
command which runs a production build.
Then for example if you scroll down it's
going to gzep all these files and build
the SSR environment for production. It's
going to remove the dev tools. Then
Nitro is going to kick in.
Basically going to split up client and
server side stuff right this all the
server side stuff is creating a JSON
file bundling for SSR has started and
then whatever folders and files it
doesn't need it has deleted it and
deployment has been finished. So if we
were to go to the link for example, go
to this specific link, it's taking a bit
longer than anticipated. And you can see
when you click on the link, it's no
longer working. You can create a
product, but when you click on the link,
it's changing the URL in the root route,
but it's not actually changing the
product. So if I refresh the page, it's
now working. So that means the linking
between products is broken. That means
we need to fix it. And I'm I have a
hunch that it is the same error that we
saw earlier wherein we are not using
loaders the right way. We need we are
somehow importing the packages a
database package on the client side. So
so let's head over to our code and here
I'm have already gone ahead and fixed
it. So there are a few things we need to
do. First off, we need to add this line
where whenever it's SSR, we need to say
exclude this package. For example, this
is extremely important. Then when we
were to go to the product card
component, which is where everything
else exists. For example, we have we are
linking the product here or let's just
go to the actual route, the dynamic
route over here. So what I've done is I
have created two separate server
functions. For the two we have get
product by ID in a separate server
function and a fetch recommended
products in a separate server function.
Now what this does is it returns the
recommended products as a promise for
suspense boundaries. If a product is not
found it's going to throw the error and
instead we're invoking server functions
to ensure server only execution and then
everything else remains the same. So
this has enabled us to So this has
enabled us to separate our client and
server environment. Now if we were to
push this and let's say we were to
create another build. So I've gone ahead
and done that as well. Take a look at
this one. This is the main deployment.
So let's just go to this site and go to
this domain. Then in that case if you
click on the link it works now. And
that's because you're going to see the
error again which says class constructor
or something like that. And whenever you
are using loaders, you have to be very
careful here. All righty. So our product
has been deployed here. Now when you go
to home, you see this little bump. You
can add a suspense boundary here for a
specific loader and display it. But the
actard button is working now. This act
button is not working. So let's just go
to ID
and this is where we have not hooked it
up. So that's one functionality that's
missing. So let's do that. Let's add the
on click handler. And we've done this
before, right? We have the product card.
And we can just pretty much copy the
onclick handler here. Go here and paste
it. Let's import the mutation. And let's
also add the two imports query client
and router. There we go. So now if we
refresh the page here, go to individual
products, add to cart, it still works
because it's the same code, right? It's
the add toart functionality that we have
implemented. So now let's go ahead and
push this change. There were also there
were also a couple of padding fixes we
can make here. For example, padding here
as well as padding in the individual
pages. So if we go down here
say that to the ID page this is good we
expand this back to products. So that
means this one say products card then
you have the suspense boundary. So let's
add some padding to the div. Add a div
around here. Let's add
MB row. There we go. Or more so MB6. Now
if you go back to products, this is
good. If you go to home, same problem.
So let's copy this MB6. Go to index page
for the more section. So I have a card
here. Add it to the grid. So I have the
card.
Add it to the grid. There we go. And
this could be MB6.
Perfect. So now it's looking really
nice. We have fixed the padding too. So
now let's just go ahead and commit the
changes. So let's go ahead and commit
the changes. Fix padding and added add
to card to ID page. All right. So now
let's go ahead and push this. The minute
we do so, this is going to kick off a
production build again. So now if we go
back to our app and refresh it here. And
now it has kicked off a production build
and it's going to build and again run
build. While this is running, let me
show you exactly what hap how it all
bundles the application for production.
So if you go to v config file over here,
we have this package for nitro that
itself is responsible for getting all
the server runtime stuff. V is
responsible for getting all the
development server stuff and it all gets
packaged together with these two
properties. All the server side stuff is
not bundled in the client side and
that's the power of using just simply
Nitro. Then Nitro even though it's used
for server runtime, we still need to
deploy it. It's not for it's not a
deployment platform. So we still need to
deploy it to a provider such as apprite
for example. We can also use the same
exact process on Versel, Netify or any
other supported any other platforms that
support Danstack start. All right. All
right. So this app is successfully
built. So if we click on the link, you
can see it has the padding we added. We
could browse different products. Add to
cart. You could go to the cart, clear
the cart completely. All the products
are gone. Click here. Add the cart.
Increments the cart. We add we've added
a couple products. So they're all listed
here. We can link. Everything is working
as expected and it's actually quite
instant. So congratulations. You have
shipped your first anstax start
production ready application. Kudos to
you for following along and learning all
the different practices for implementing
a new framework.
Now, if you've enjoyed watching this
specific course, definitely hit the like
button, subscribe to my channel as I
plan to create a video every single week
until Christmas. If you like content
like this, comment below and let me know
what is it that you love about Danstack.
if you still prefer Next.js and so on.
Now, as next steps, if you also want to
learn how NextJS works, you could also
check out this Nex.js 16 crash course
that teaches you everything that you
need to know to master Next.js. Just
like we build a application, a
production ready application inside of
the Tanstack start course. Similarly, we
also build another application inside
the NexJS course as well. All right,
thank you so much for watching.
⭐ Download Github Source Code and All resources here: https://dub.sh/tanstack-course-cheatsheet In this 4-hour long TanStack Start full course, you’ll build and deploy a production-ready ecommerce app while learning how TanStack Start, TanStack Router, TanStack loaders, and TanStack Query work together. This course explains TanStack Start vs Next.js, modern React full stack framework architecture, and how tools like Vite, Nitro, and Vinxi power real-world React application and how to ship a production-ready app with TanStack Start. You'll also learn TanStack Router, TanStack Query and TanStack Form as part of this course. If you want to understand the TanStack ecosystem by building a real app, this course is for you. ‼️ This course is not sponsored and took a significant amount of time to create, so if you found it helpful, I’d really appreciate it if you could hit the like button and subscribe for more content like this. What to Watch next? https://youtu.be/tI_Nt32_4wM 🤝 Need Help or want to connect with others just like you? Join the community: https://dub.sh/anky-discord 🛠️ Core Technologies: - 🚀 TanStack Start for full-stack React with server-side rendering and API endpoints - 🧭 TanStack Router for type-safe, file-based routing with automatic code splitting - 🔄 TanStack Query for powerful data synchronization, caching, and server state management - 💾 PostgreSQL database with Drizzle ORM for type-safe database operations - 💿 Supabase for PostGres Database - 📦 Server Functions for secure, type-safe server-side operations - 🎨 ShadCN UI for accessible, customizable React components - 💅 TailwindCSS 4 for utility-first, responsive styling - 📜 TypeScript for static typing and enhanced development experience - 🔧 Vite for lightning-fast development and optimized production builds - 🧪 Vitest for unit testing with React Testing Library - 🚀 Appwrite for deployment 💫 Application Features: - 🛍️ Complete shopping cart functionality (add, update, remove items) - 📊 Product catalog with filtering and search capabilities - 🎯 Product detail pages with images, descriptions, and ratings - 🏷️ Product badges (New, Sale, Featured, Limited) - 📦 Inventory management (in-stock, backorder, preorder) - ⭐ Product ratings and reviews display - 🔍 Recommended products section - 🎨 Beautiful, modern UI with smooth animations - 📱 Fully responsive design for all devices - 🔄 Real-time cart updates with optimistic UI - 🚀 Server-side rendering for optimal performance - 🔐 Type-safe routes and API endpoints - 📈 SEO-friendly product pages with meta tags - 🎯 Loading states and error handling - 🔔 Toast notifications for user feedback #tanstack #react #fullstack #nextjs #typescript #javascript #fullstack 00:00 - Course Introduction 00:39 - Tanstack Ecosystem Overview 01:25 - Tech Stack Overview 02:22 - Download CheatSheet 02:39 - Start Shop Demo 03:14 - Products Page Demo 03:59 - Product Detail & Cart 04:26 - Recommended Products 06:09 - Shopping Cart Features 07:11 - Create Product Demo 08:29 - Technical Features Summary 09:55 - Demo Conclusion 10:22 - What is Tanstack Start? 11:15 - Tanstack Router Deep Dive 13:15 - Vite for Development 14:04 - Nitro Server Runtime 15:10 - Winji Orchestration Layer 15:56 - Why Tanstack Start? 19:59 - Start vs. Router 20:21 - Create New Project 22:20 - Project Structure 24:57 - Tanstack Routing Basics 27:49 - Link Component Features 29:57 - Vite Config & Plugins 31:19 - File-Based Routing 32:14 - Dynamic Routes 34:15 - Project Cleanup & Header 36:16 - Shadcn UI Integration 39:52 - Header Component Styling 46:47 - Homepage Hero Section 56:15 - Data Fetching Strategies 58:44 - Isomorphic Loaders 01:01:20 - Fetching Homepage Products 01:06:50 - Server Functions Explained 01:12:20 - Product Card Component 01:23:25 - Products Page & React Query 01:29:25 - Middleware in Tanstack Start 01:37:09 - Selective SSR Options 01:45:52 - Dynamic Product Page Setup 01:48:03 - Database Setup with Drizzle 01:59:59 - Loader Database Fixes 02:07:39 - Product Detail Page Build 02:12:44 - Dynamic Metadata & Suspense 02:23:25 - Create Product Form 02:50:53 - Cart Page Layout 02:52:50 - Fetching Cart Items 03:01:22 - Cart Quantity Management 03:19:59 - Clear & Remove Cart 03:28:06 - Cart Code Refactoring 03:30:00 - AppWrite Deployment 03:35:16 - Fixing Deployment Errors 03:40:44 - Final Demo & Conclusion