Loading video player...
Is it just me or is there an increasing
number of those 15-hour long tutorials
where you spend more time building than
actually understanding what you wanted
to learn? Today, we're going to end that
by building a crypto screener app with a
built-in highfrequency terminal and
dashboard. It's powered by the Coin
Gecko API handling the most volatile
data on the planet in real time. Using
it, you'll build a homepage that
highlights live discovery of trending
assets and categories driven by realtime
market momentum. A global explorer coin
feed featuring a comprehensive all coins
view with rankings, prices, and
efficient serverside pagenation. an
analytics deep dive for each asset where
you use Coin Gecko's websocket API
enabling real time subsecond candlestick
charts onchain trades and price updates
through persistent data stream
connections with built-in pingpong and
autoreon and a global command pallet
that replaces traditional menus with
control or command K interface for
instant marketwide search. We aren't
just vibe coding, we're engineering. And
because of that, by the end of this
video, you'll understand how to fetch
large data sets on the server while
keeping the client lightweight. How to
run parallel data requests without
blocking the UI. How to combine
serverside data fetching with client
side revalidation and real time UI
freshness. How to use streaming and
suspense so pages render progressively
as data arrives. How to manage complex
flows with server actions cleanly and
predictably. How to build custom hooks
and a common reusable fetcher function
for cleaner and more maintainable data.
How to create reusable advanced UI
components that scale across the app
while keeping it fast and responsive
under heavy data load. Finally, you'll
learn how to use the websocket API to
display live streaming data and update
the UI in real time. All while thinking
about performance, scalability, and
architecture in real Nex.js apps. Once
you learn these, you'll be able to work
on any NexJS application comfortably.
And alongside Nex, you'll also use
TypeScript, Tailwind CSS, Shat CNN,
Charts, and Coin Gecko API, the most
trusted platform for crypto data. and my
go-to cryptodata API for building web 3
and crypto projects. You can build
almost everything in this project on
their free demo plan with up to 10,000
API calls per month, but I'll also teach
you how to use Coin Gecko's API analyst
plan for those instantaneous websocket
updates. You can use the JS Mastery 10
code for a discount on a paid plan if
you decide to go for it. The link is in
the description. So, if you're a
beginner to intermediate developer who
wants to learn how to build real NexJS
applications from a Figma UI and an API,
this video will give you everything you
need. And if you itch to build something
more advanced like full serverless, full
stack development with Nex.js, you can
check out my ultimate Nex.js course and
complete testing course for production
level learning. The link is in the
description. Go grab your coffee and
let's ship this
Let's start developing coin pulse by
creating a new Nex.js application. In
this course, you'll learn how to
structure a modern app using app router
and build reusable UIs with chat CN. So,
let's get started by spinning up a new
Nex.js application. I'll start by
opening WebStorm and creating a new
project. I'll create it as a new empty
project and call it coin pulse. We'll
create a git repo manually later on and
I'll set it to TypeScript. So, let's
create this. Will create a new folder
and if it's not empty, you can clean it
up and open up your integrated terminal.
Within it, you can paste the command we
just copied. MPX create next app add
latest and then add dot slash to create
it in the current repository and press
enter. It'll ask you whether you want to
install the create next app installer.
So say why yes
and then you'll be asked a couple of
questions. Do you want to use the
recommended settings including
TypeScript ESLint Tailwind CSS and app
router? And to that we'll say absolutely
these are all the tools that we need to
build modern scalable applications. So
press enter and within a couple of
seconds next.js installer will install
only three primary dependencies next
react and react dom and a couple of
additional dev dependencies needed to
improve our developer experience. As for
the recommended options that we chose
the reason why we chose them is that
TypeScript gives you stronger safety and
better developer experience. The app
router is the current Nex.js standard
for layouts and streaming and pill and
CSS will help us keep our styles
scalable without heavy CSS files. Once
it's installed, you can simply run mpm
rundev to spin it up on localhost 3000.
Then back within the browser, you'll be
able to see a Nex.js boiler plate that
looks something like this. Now, before
we start editing the files, I want to
quickly go over the capabilities of the
IDE that I'll be using throughout the
rest of the build. and you can too. It's
WebStorm and as of recently, it became
completely free for non-commercial use,
which means that you can now use it to
develop your projects. So, click the
link down in the description and
download it to be able to follow along
and see exactly what I'm seeing. Another
reason why I love using WebStorm is that
you can pair it with Juny. Juny is a
coding agent developed by Jet Brains
that makes you that much more
productive. Throughout this course,
you'll see me use Juni a couple of
times. Sometimes when I get stuck or
sometimes when I just need an extra push
to develop the code faster. You can also
download it right now so that later on
we can just use it together. With that
in mind, let me give you a quick tour of
the generated file and folder structure.
We'll spend most of our time within the
app folder. Here, every route will be a
folder and every folder can have pages,
layouts, and more. Currently, we have
only one page, which is the homepage.
Then there's the layout within which we
set up our fonts and our site metadata.
Here we can also define that our page
will be rendered in a dark mode for
example because whatever you do here
will be applied across the entire site.
Then there's the globals.css which acts
as the Tailwind CSS v4 entry point
because we're importing Tailwind and
setting up our theme and the public
folder we have our assets. Then there's
the next config where we can create our
Nex.js JS configuration for image
domains, experimental flags, build
settings, and so on. There's also a
package JSON for all of our scripts and
dependencies, package lock to keep track
of those changes, as well as a TS config
for TypeScript related settings. Now,
let's set up SHAT CN. Open up your
terminal. And thankfully, Webtorm allows
me to have multiple terminals running.
So, I will rename this to app. And then
I'll create a new one, which I'll simply
call terminal. And within it, I will run
mpx shhatzen at latest in it, which will
ask me a question whether I want to
install the shhatsen installer. And then
it'll ask me a couple of questions on
the styles that I chose, such as the
color that I would like to use. In this
case, we can go with neutral. And to
just verify if it's working, we can head
back over to our app page. DSX. Remove
everything from there and simply type R
A FCE, which is short for React arrow
function with export component. And if
this didn't work for you, you might need
to head over to plugins or extensions.
And then search for React snippets. In
this case, I'm using modern React
snippets, and they allow me to quickly
spin up new components. Within here, we
can simply return a p tag that'll have a
class name of text-3xl
and text- indigo 500. This will change
the color and make the text large. And
I'll make it say coin pulse. If you've
done that properly and head back, you
should be able to see a purplish large
coin pulse text right on your homepage.
Let's also open up our layout and modify
the metadata of our application. I'll
simply call it coin pulse. And within
the description, we can type something
along the lines of crypto screener app
with a built-in highfrequency terminal
and dashboard. So now, if you go back,
you should be able to see that the title
of your application change. And very
soon, we'll also change the favicon to
match the one of the final website,
which you and I will build together
very, very soon. And now that we've
successfully set up our application, it
makes sense to create a new GitHub repo
for it. So we can consistently from the
get- go commit and track our changes.
I'll call it Coin Pulse and simply click
create repo. Next, we can follow the
steps to get it committed. And this
might be a good idea to put our browser
side by side with our editor. That'll
look something like this. So now within
the terminal, we can follow the steps to
get it committed. I'll first say get
init add dot to add all the changes get
commit-m first commit get branch m to
create and check out to the new main
branch add a remote origin which will
connect the local version of this repo
to this one on github and finally get
push u origin main which will push the
code that we have created right over
here to our github repo. You should do
this at the start of every single
project because sharing your work makes
all the difference. Potential employers,
clients, companies, and other developers
can find you like this and check out
your work. Not only this allows you to
keep track of your commit history and
show everybody that you're the one
actually doing those refactors, but it
also allows you to include code rabbit
right from the start. Nowadays, I don't
create any projects without it because I
like to move fast. But still, I don't
want things to break. So, I always set
it up from the start and then later on
whenever I make some important changes,
I open up a pull request and let Code
Rabbit review them. I'll show you how
you can do that too later on. For now,
you can just click the link down in the
description, click try it for free, and
then create your account using GitHub.
As soon as it loads your workspace,
you'll be able to see all the other
repositories that I added or connected.
And then you can also click the add
repository button to add the current
repo that we'll be committing our code
to. With that in mind, now that the
setup and the boring part is done, let's
move from turning this into something
that looks more like this.
In this lesson, we'll start by
developing our simple navigation bar. So
back within our code, let's create a new
branch for it by running get checkout
dashb feet as in a new feature we're
adding and then navigation. This will
create as well as checkout to this new
branch. Now let's start by understanding
our design a bit better. I haven't just
coded this out from my head. Instead,
our designer prepared a complete crypto
platform design. I'll share it with you
for free within the video kit link down
in the description. That way you can
explore it, see how it works, and maybe
even download some icons such as this
coin pools logo that we've created as
well as some other icons that we have
right here. What you can also do is pay
attention to the color scheme. So, if
you click on the dashboard or on a
specific part, you'll be able to see the
color that we used for the background as
well as for the text. The whole app is
dark. We have the white text for the
primary text color, but also the
secondary text color right here. And
then of course there's this green color
that you'll often see popping up. Now
the goal is to take a couple of these
primary colors and put them within our
globals.css which will act as the theme
for our application. You can see the
theme that Shad Cn created already, but
we don't need none of that. So you can
remove it and instead click the video
kit link down below. find crypto pools
and right below here you can find the
snippets that I've prepared for you. So
simply copy the globals.css and paste it
right here. It might seem like a lot but
most of it is just setting the theme as
well as specifying some helper utility
classes that we'll use later on
throughout the application such as the
containers badges headers and so on. The
main goal of this app is to help you
understand nextg features like streaming
and data fetching not tailwind and CSS.
If you want to learn Tailwind, I've
already created a full video on it. I'll
leave a link down in the description so
you can check it out if you're not
comfortable with it yet. Still, of
course, I'll explain the styles as we
go. I just won't be writing them myself.
Let me show you what I mean. We'll
create our first component of the day.
So, right here within the root of our
application, create a new folder and
call it
components.
Within components, create a new file
called header.tsx.
You can see that webtorm automatically
asks me whether I want to add it to git.
I'll say don't ask again. And for now,
we don't need to add it. We'll do that
manually.
Within it, you can run rafce to quickly
spin up a new header component. And then
we want to import that header within our
layout because the header will be
showing on every single screen. So we
want to display it right here above the
children by simply referring to the
header component which will also autoimp
import it from components header. And
another important change we have to make
within our layout is to apply a class
name set to dark to the entire HTML
website. If you do that, you'll notice
that the background will immediately
turn over to dark mode. Now head over to
the header and let's get it implemented.
I'll start by turning this into a
semantic HTML 5 header tag. Within the
header, we'll define a div that'll have
a class name set to main-container
and it'll also have a class name of
inner. Now, this is not a main shaden
class name, but the one that I created
before. So to be able to see what it
actually means, you can search your
codebase for main container and you'll
find this layer components main
container where we're applying a padding
Y of six to give it some vertical space.
I'm also making it more responsive for
medium devices and small devices. Giving
it some max width and some spacing. Now
within it we can render a link component
coming from next link that'll have an
href pointing to forward slash which is
typically the homepage and within it we
want to display our logo. So I will
import the image component coming from
next image with a source of
for/assets/logo.svg
and an all tag of coin pools logo.
Finally, we'll give it a width of about
132 and a height of about 40 pixels. If
you save it, you'll see that we cannot
yet find the Coin Pool's logo. So, the
way you can get it is by going over to
the Figma design, clicking a few times
right here until you get to the logo and
export it in SVG. You can actually grab
the whole part, not just the image, but
actually the entire frame containing the
word coin pulse. Alongside exporting
that, I'll also export this little icon
right here, which is going to be used
for our currency converter. So, go ahead
and click until you get to the image and
then export it right here also as an
SVG. Once you export both, you can head
over to your public folder and delete
all the files that are within it and
simply drag and drop the two new icons
that you downloaded.
One is going to be the logo and the
second one is going to be the exchange
icon which you can rename to converter.
SVG. Now if you do that you can just
remove the assets part of the logo
because the logo is directly within the
public folder and it'll show up. Nice.
Now we no longer need this coin pools
text right here. Rather we can return an
empty div for now. Now back within our
header, let's continue with the main
part of our navigation. Still within the
same div, I'll create a new nav tag
within which I'll create a new link.
This link will have an href pointing to
forward slash. Yep, it'll still be the
homepage and it'll simply say home. Then
below it, I'll do a p tag that'll say
search modal, which will be a trigger
for opening the search modal. We'll work
on that soon. And then also another link
that'll have an href pointing to
for/coins which will open up the
research page for all of the tokens. So
we can say all coins. Now we have to
figure out which one is currently
active. And to be able to do that we
have to use browser's URL
functionalities. But Nex.js pages by
default are serverside rendered which
means that they don't have access to the
URL. So if you want to use the
functionality something like const path
name is equal to use path name coming
from next navigation you also have to
add a use client directive at the top to
turn this into a client side rendered
component. Now that we have it we can
head over into each one of these links
such as this one pointing to the home
and open up a new class name within it.
I'll use CN which is short for class
names which allows us to put dynamic
links. It'll always have a class name of
nav link but then dynamically we can
apply the is active
class name only if path name is set to
the forward slash and I'll also apply
the is home class name which will be set
to true in this case. So now you can see
that on the smaller devices it won't be
here. But if we go larger it'll be
there. We can do the same thing for the
coins page. So I will simply copy this
class name and paste it to the coins
link. But this time is active will be
true only when we are on the coins page.
There we go. Of course if you click to
it now you'll see a four or four. But
you can see that it is active when we
are on that page. And when we're on home
you can see that this one is white. And
with that, we've implemented our first
very simple navigation component. Not a
lot of logic is happening here, but hey,
step by step, we'll get there. This
concludes our navigation feature. And
with that also the navigation branch.
So, if you open up your terminal, we can
now commit the changes that we've
implemented by running get addit
commit-m and I'll say implement
navigation
and run get push. Since this is the
first push on the new branch, we'll have
to copy and paste this set upstream
method. Once that is done, you'll see
that our navigation branch had recent
changes 13 seconds ago. So, even though
this is a super small example, it is a
perfect opportunity for me to show you
how the rest of the workflow of this
great application will look like. We
open up a branch, start developing the
features. Once they're done, we compare
and open up the pull request, create
that pull request, and if you added Code
Rabbit to this repo, it'll very quickly
start repairing the report for you. In
this case, it has just one to do to find
more bugs. So, you need to give it a
minute. I think in this case, since it's
a super small PR, it might take just a
couple of seconds. And immediately,
you'll get a walkthrough of the changes
that were implemented. And once again,
you should always start reviewing those
changes from the start. so new bugs
don't sneak in. And I'm glad that we
have a super simple example to show you
how all of this works. In this case,
we're simply introducing a new header
component with navigation, integrating
it into the main layout with dark mode
styling, expanding the global CSS with
comprehensive color variables and UI
component styles. And we simplify the
homepage to an empty div. We see the
summary of changes per file or per
cohort of files. You get the estimated
code review time. In this case, it's
moderate because this globals.css does
contain quite a few class names. And
then we have a simple header. In this
case, I'm assuming we won't get any
major bugs, but a couple of nitpicky
comments such as these ones. But would
you look at that? There is one huge
potential issue. And it says that for
assets in the public folder, Nex.js
expects an absolute path starting with a
forward slash. So using just logo.svg
SVG may cause a 404 or resolve
incorrectly depending on the current
route. Okay, this is actually huge. It
then gives you a proposed fix so we can
fix it. In this case, it cannot be any
simpler, but rather just adding a simple
character and then it also provides you
with a commitable suggestion in case you
want to do it right away. But if it's a
bit of a more complex fix and you want
to run it by somebody else, it also
gives you a prompt for an AI agent to
fix. In this case, it might feel really
silly to try that out because we only
have to add one character. Come on,
we're not that lazy. So, back within the
code, I'll head over into our header.tsx
and I will add a starting forward slash.
Then, I will simply run get add dot
getit commit and say fix code rabbit
suggestion
and get push. As soon as you push this
PR should update. The code should be
rechecked. But in this case, I believe
we're more than safe to merge this pull
request and continue developing the
other features of our application. All
of which are going to be about crypto
market data.
In this lesson, we'll set up the Coin
Gecko API so it's easier to develop the
UI alongside displaying the real data.
Coin Gecko is my go-to source for crypto
market data and it's been a trusted
standard in the space since 2014 and
they're kindly sponsoring this video. So
besides learning XJS using this API,
you'll also learn how to develop tools,
dashboards, and screening monitors to
discover market insights, track trends,
and understand how realworld crypto data
is analyzed in production applications.
You can start using Coin Gecko by
heading over to API pricing. Then, if
you want to try it out before
subscribing, you can create a free
account and you'll be able to develop so
many of the functionalities from this
application entirely with a free
account. You get 10,000 calls a month
with a rate limit of 30 per minute. I'll
show you how to make the most of the
demo API, but also later in the project,
I'll show you how to use the analyst
plan that'll give you access to 10
websockets, which we'll use to build a
realworld crypto screener focusing on
real time data. For now, go ahead and
start with a demo account by clicking
create free account right here. I'll
leave the link down in the description
and then simply join in with Google or
Apple. To create your API key, simply
click your profile and then developers
dashboard. And here you can add a new
key. You can give it a label such as
Coin Gecko and click create. Then you
can simply copy it and add it within
your application specifically within a
new file that we'll create in the root
of our application that I'll
call.env.local.
So here we can create a new coin gecko
API_key
which will be equal to the key you just
copied. We'll also duplicate this and
allow it publicly so we can access it
from a next public endpoint which will
give it access on the client side as
well. Finally also define the coin gecko
base URL by making it equal to https
col//pro-api.cocko.com.
at coiningo.com/api/v3.
We'll use this later on. And now that
you have your own coin gecko API key, we
can turn this into this. So immediately
in the next lesson, let's dive right
into implementing our homepage.
Now that we've got the API key, you can
head over to your app page and we can
get started developing it. We'll first
develop the layout. So I'll turn this
div into a main tag and give it a class
name set to main container. Then within
it I'll create a new section. And that
section will have a class name set to
home grid. So we can create the grid of
our homepage. Within it we can start
creating the layout by creating a couple
of different P tags. We'll have our coin
overview which is going to show the main
information about the coin. Then we'll
have another P tag that'll show all of
the trending coins. And finally below
this section, we'll create another
section that'll have a class name set to
W full. So this one is going to take the
full width of the screen with a margin
top of seven to divide it from the
previous section and a space Y of four
in between the elements. And this one
will render the name of the category
that's going to be well categories. So
it'll show different categories of
cryptocurrencies. So now on small
devices, you can see they all show up
one below another. But if you go to a
desktop screen, you'll see that the coin
overview and trending coins will show
one next to another, whereas the
categories will take the full width of
the screen exactly like it does on the
final application. Now we can create the
UI for the coin overview by removing a P
tag and turning it into a div that'll
have an id of coin or view.
Then within it we can display another
div that'll have a class name set to
header.
Within the header we can display an
image. And for the source for now we'll
make it static but later on we'll make
it dynamic depending on which coin we're
checking out. So right here, the source
will point to https
col/assets.coin.comward
slashcoins
slashim images
slash one large bitcoin.png.
So for now, we're going to obviously
display bitcoins logo. So that's exactly
what I'll type right here. and you'll
get an error saying that you cannot yet
display this image right here. That's
because within our next config, we have
to allow the assets.cocko.com
endpoint to be able to display images.
So simply say images
remote patterns
and then within it add one that has a
protocol of HTTPS and it has the host
name set to assets.cocko.com.
If you do this and add a width and a
height to this image, such as 56 and 56,
you'll be able to see a Bitcoin logo
appear right here. Then, right below it,
let's also render another div that'll
have a class name set to info. And
within it, we can display a P tag
that'll say the full name of the coin
such as Bitcoin and then BTC, which is
the ticker, and then an H1, which is
going to render the current price of
Bitcoin. At the time of the recording,
it is about 89K.
So, I'll set it right here. And we can
also give this div a class name of
header, but alongside it, a padding top
of two to make it look good. And that's
it for the primary coin display. But
later on below this Bitcoin, let's spell
it properly, will display an entire
chart which will be updated in real
time, allowing you to choose the period
as well as the update frequency. But for
now, let's move on to developing the
trending coins. Throughout this
application, there's going to be so many
different tables. We're going to have
one here, one there, and there's going
to be another one in all coins. That
means that it makes sense to take some
time and really try to approach the data
table component in a way that it's
reusable and that it looks good on all
the different places where it's used.
That way we'll avoid writing the same
boilerplate code over and over again. So
first let's install a table component
coming from chats UI. We can install it
by running this command right here that
is mpx chat at latest add table. Once we
add it, we can create a new component
for it within the components folder
where I'll create a new file and call it
data table.tsx.
And within it, we can follow the steps
needed to recreate a chassen table. And
then we'll modify it to our use case.
First, I'll copy all the imports, then
run rafce,
and then copy the actual usage of the
table right here. What you can do then
is simply import it within your page.
So, right here below trending coins, I
will import the data table
coming from our application. And if you
get back, you'll be able to see this
demo table coming from SHAN. This table
right now just displays one table. So if
we reuse it on a second page, it'll
still show the same data. We have to
somehow extract that data and we need to
be able to pass it into the component to
show different data depending on where
it's getting called. That means that
we'll have to pass all the new columns,
the data to fill in those columns, the
row key, which is a function to generate
a unique key for each row, an optional
table class name in case we want to
style it differently, a header row class
name, a header cell class name, a body
row class name, and finally a body cell
class name. Now, since we're defining
all of these different props, first of
all, they have to be within curly braces
as we're dstructuring them from props.
And then we have to define the type of
all of these properties. So something
like defining that columns is going to
be maybe an array. Then data is going to
be an array of different objects. And
then we have to define the types of all
of these properties. So instead of
writing it all right here, what you can
do is create a new file called type d.ts
within the root of the application. This
is a special file and d stands for
declaration which means that we'll
declare all of our types right here.
Within it, you can start defining
different interfaces. Interfaces are
simply object-like types. So interface
you can call it data table props and
within it you can for example say that
the table class name is optional and of
a type string and then you have to
repeat this for all of the other class
names and then you'll be able to declare
all of these types directly by referring
to the prop. So data table props like
this. Of course right now it doesn't
know about all of these other ones
because we haven't yet declared them.
So, just so you don't have to write all
of these types yourself, I have actually
gone ahead and provided them for you
within the video kit link down in the
description. So, under snippets, you can
find types.d.ts
and simply copy and paste them here.
There you'll see the data
table props containing the data table
column, the data itself, row key, and
all of the other class names.
If needed, occasionally I'll go back to
this file and explain exactly how I came
to these types. One thing that we also
have to do is make this data table
accept an additional type that we're
then passing over into the data table
props. Now, in this case, we also have
to pass an additional prop called T into
the data table props. You can do that
like this. Define it here and here. Now
this file and our entire application
knows exactly what type of columns is
going to be the format of the data the
row key which we have to spell properly.
Same thing for the body row class name
and now we can use them across our
application. If you want me to do a
fully typed version where we write all
the types from scratch, just let me know
down in the comments below and we'll do
a proper TypeScript crash course. Now,
with that in mind, let's turn this basic
table into a table that's fully
reusable. First, I'll give it a class
name set to CN. So, we can pass multiple
class names into it. The first one will
be custom scroll bar. So, we give it
that hint of green. Don't forget to
import CN coming from lib utils. And we
can also pass this new class name that
we'll create called table class name. In
this case, we can skip the table
caption. But for the table header, we
can simply provide a new class name of
header class name. And I think we
actually missed this one when defining
all the props that we'll need to pass.
Then right beneath the header, we have
the row. So let's give a new class name
to this row of CN on hover. We want to
change the cover to transparent. So I'll
say bgt transparent exclamation mark.
And then we'll also apply the header row
class name within it. You can see that
currently we're displaying a couple of
different table heads such as the
invoice status method and the amount. In
this case, we'll want to map over all
the columns that will dynamically pass
over through props. So let's remove
these table heads and instead let's map
over the columns by saying columns.m map
where we get each individual column and
its index. And for each one we
automatically return a table head that
will then render the column header
information.
So when we're calling the data table
specifically right now that is within
our homepage we'll have to pass in the
columns data and for now I'll set it to
an empty array. Later on we'll fill it
with real data. This table header needs
to have a key since we're mapping over
it. And that's going to be an index for
now. And a class name set to we'll do uh
CN so we can provide multiple class
names.
We'll provide a bg dark of 400,
text-purp,
padding y of four.
Only the first element will have a
padding left of five. And the last
element will have a padding right of
five as well. You cannot see those yet
because our column headers are empty.
But if I go back here and provide a
header for our first column to something
like let's do a title of the coin. You
can see how this is going to look like.
And then we can duplicate it and provide
a second one which is going to be maybe
the price of the coin. And you can see
how that's going to look like right
here. Then right below the table row for
the table headers, we can go down and
focus on the table body. Within here,
we'll map over the data by saying data.m
get each row and its index. And for each
one, we can automatically return a table
row. Each table row will also map over
the columns. So I'll say columns.m map
where we'll display a table cell. Each
table cell will then render its contents
by saying column dot cell will pass over
the data for the row and the row index.
This row index is coming from here.
Of course, data doesn't yet exist. So
very soon when we get the real data
coming from this table, we'll also need
to pass along to the columns the data to
fill in those columns which is also
going to be an array. Then since we're
mapping over our table rows, let's
provide a key to each one. And to make
it super unique, we'll pass it into this
row key function where you pass the row
and the row index. Then we can give it a
class name set to CN. And we'll pass
some class names like overflow hidden
rounded-l
border-b so we can see the
differentiation between each row
border-purp
on hover. We'll change it to background
dark 400 over 30.
And it's very important to make it
relative. That way it's tracking your
cursor and you can actually see as
you're hovering over different rows.
After all of these class names, I'll
also provide the body row class name.
This one is going to be unique depending
on what we pass in the props. And
finally, we'll do a similar thing for
the columns. I'll rename this I to
column index. And we'll provide this
table cell a key of column index as well
as a class name of CN. And then we can
pass a padding Y of four to create some
vertical spacing.
will give to the first element a padding
left of five and to the last of padding
right of five. This is going to make so
much more sense once you actually see it
on the screen. So now that we have this
what seems to be a reusable table, we
can head back over to our homepage and
actually put it to use. We'll need to
create a format for the data that we
need to pass into this table. So let's
do it right here above the page. const
columns
of a type data table column which will
take in the trending coin coming from
types specifically an array. It'll be
equal to an array where each specific
column will contain a header.
For example, the first one will be the
name of the coin. Then it'll contain the
cell class name that'll be set to name
dash- cell. And it'll contain the cell
information itself, which in this case
will be set to a function where we
accept the information about the coin.
And then let's actually put this into
multiple lines so it's easier to see.
Once we get it, we want to extract that
item that we're displaying. So that's
going to be coin item. And then we want
to return in this case not just a title
saying the coin name but rather we'll
link to the details page of that coin.
So we'll have a complete link
coming from next link with an href of
forward slashcoins
slash item id. Of course we have to turn
this into a dynamic template string just
like this. And then within this link, we
can render an image with a source of
item.large.
An al tag can be the name of that coin.
So that's going to be item.name. A width
of about 36. A height of 36 as well. So
far, we have no data to display, but
trust me, it'll appear right here soon.
And finally, below the image, we can
render the actual name of that coin. So
that's going to be within a p tag
where we're going to render the
item.name. Now we can go below this cell
right here and we can add another
column. This one will have a header of
24-hour
change.
So how did the price move in those 24
hours? And finally, what will display
within that cell? We'll get the
information about the coin. And then
we'll open up a function block within
which we can extract that item from coin
item once we actually fill it with data.
And we'll figure out whether it's
trending up or down. So const is
trending up is equal to item data.
Underscore change percentage_24
hours.USD.
And if it's greater than zero, that
means that it is trending up. Finally,
that gives us the info to return a div
displaying the corresponding change. So,
I'll give it a class name of CN and
it'll say price change. So, that's going
to be the general class name. But within
the CN, if it's trending up, if it's
trending up, then it'll have a class
name of text- green 500. else it'll have
a class of text- red 500 and don't
forget to import CN because we're now in
a different file within it we can
display a P tag within which if it is
trending up in that case we'll display
an icon coming from Lucid React so let's
install that package it's maybe the most
popular icons package on mpm it's called
lucid react
and once you install it you can just
start typing trending up and simply
import it from Lucid React. We can give
it a width of about 16
as well as a height of 16 as well. And
likewise, I'll duplicate it. Now, we
want to see what's going to happen if
it's trending down. In that case, we can
just say trending down and import that
icon from Lucid React. Of course, this
import has to come all the way at the
top. trending up and trending down.
Let's make sure to properly close it.
And again, we have no data yet, but
we'll have it very soon. Just one more
header to show, and that header is the
price. So another object with a header
called price, a cell class name of price
cell and then a cell itself which
gathers the information about a coin and
automatically returns the price of coin
item
data.
This one is a bit simpler than the
others, right? Okay, great. So now is
the time that we feed some kind of data
into the table to be able to see it.
We're going to gather tons of data from
the Coin Gecko API, which means that
we'll create a reusable fetcher function
in a new file to fetch everything in a
super simple way. But before we dive
into implementing that, I still want to
see how this table would look like with
some fake data, just until the real data
comes in. And this is one of those cases
where AI can help you in a matter of
seconds. In this case, I'll be using
Juny. So, if you haven't yet installed
it, click the link down in the
description and just install it and open
it up. And once it's there, let's tell
it something like review app page.tsx
and add a local dummy
trending coin array
data set
matching the trending coin type. So
you'll be able to use our TypeScript
types to define that data set. Use
existing local image assets for thumb
and large. So images resolve. Wire the
dummy data into the data table. So the
table renders rows. I think if you stop
and retype this into Juny, it should be
able to create some kind of a data set
for us. Let's see how well it does. It
looks like it's understanding what we're
doing. So it's updating its own task
progress. The objective is to add dummy
data to the data table. It managed to
find the file and update it. There we
go. It's editing the page as we speak
right now, adding the dummy trending
coins such as Bitcoin and Ethereum in
this case. And it should also be able to
automatically put them into the table.
There we go. It added the dummy trending
coins with the columns, the row key, and
the table class name. Wonderful. So if I
collapse this right now, would you look
at that? We can already see the data
within the table. Under name, it shows
Bitcoin. And it looks like it used the
coin computer logo as the logo instead
of the actual Bitcoin logo, which is
totally fine for now. We're going to
replace this later on once we get the
real data. And then it's also displaying
the 24hour change, whether it's trending
up or down. Wait, is it just me or did
Juny just fix this part as well? Because
here before I don't think I used the
absolute value and two fixed percentage.
I believe we just had it written like
this which is not as precise. So this is
definitely better. So now that we have
the homepage that looks like this but
still displays dummy data, it deserves
to get the real data fetched from the
Coin Gecko API. So in the next lesson,
let's build a fetcher function that
allow us to do just that.
In this lesson, we'll start fetching
real data and display it on our
homepage, and we'll do that with Coin
Gecko's demo API. I'll leave all the
relevant links down in the description,
but in a nutshell, this demo API allows
you to call many different endpoints.
Everything from a simple ping to
checking the prices of coins, as well as
listing all of the supported coins and
their markets. There's a lot of stuff to
explore, so let's dive right into it.
First things first, I'll install an
additional package that will help us
with creating different URLs. MPMI
query-string.
It'll allow us to stringify URLs. Then
we can create a new file within the lib
folder. It'll be called coin
gecko.actions.ts.
This will be a server file. So we can
use the use server directive because
it's only going to be called from the
server. And within here, we'll implement
all the data fetching logic for all the
different Coin Gecko API endpoints. I'll
also import QS from query string so we
can stringify our URLs later on. And
let's also get access to our coin gecko
base URL by saying const base URL is
equal to and we already stored it in our
process.env
under coin gecko_base
URL. We can just check it to verify it
is Coin Gecko base URL. And we need to
do the same thing with the Coin Gecko
API key. So I'll duplicate this. Go to
the process.env
Coin Gecko API key. And I'll rename this
to API key.
If we don't have access to the base URL,
that means that we cannot do anything.
So let's immediately throw a new error
saying couldn't get a base URL and I'll
duplicate that and do the same thing
with the API key if there is no API key
could not get API key. Perfect. So this
will save us in case we cannot properly
get it from environment variables. And
now we can focus on the most important
function of this file. The one that's
going to be responsible for fetching all
of the different endpoints. I'll export
it. So, export async function and I'll
call it fetcher. And I'll define this
new dynamic type that we'll pass into it
of t depending on what we're fetching.
And it'll take in a couple of
parameters. It'll take in the endpoint
that we're trying to fetch of a type
string. It'll also take the params that
are going to help us fetch specific
things. These are going to be optional
and of a type query params. And finally,
we want to revalidate it. So I'll say
revalidate to 60 seconds in case we
don't revalidate it sooner. Finally,
this function will return a promise
because we're making an asynchronous
call. And then we can open it up right
here. So first things first, we have to
figure out which URL we're trying to
call. It's not just as simple as
defining URL and making it equal to the
base URL. We have to construct the URL.
So that's why we're using this QS as in
query string where we can stringify a
URL by connecting together different
parts. First we pass the URL object that
contains the URL itself which is going
to be a concatenation of the base URL
and the endpoint that we're trying to
call and then to it we can also append
additional queries which are going to be
params. So essentially we're
constructing this coin gecko starter
part and appending the endpoint like
coins as well and potentially passing
some additional params like I don't know
the ID of our account or the API key
equal to the API key. I'll show you how
we're going to connect it all very soon.
And we can also pass an additional
options object right here that's going
to say skip empty string and set it to
true so that we don't have to manually
remove the parameter keys if we pass
empty values as well as skip null values
set to true. Perfect.
Now we have to make a call to this URL
and for that we'll use a regular fetch
object. So I'll say const response is
equal to await fetch the URL and then
we're going to fetch it but at the same
time we're going to pass a few headers.
We need these headers to be able to make
a valid request such as XCG Pro API key
which we're going to set equal to the
API key that we got from the Coin Gecko
API and then also the content type which
we'll set to application JSON as that's
how APIs most commonly communicate. Now
I'll define the type of these headers.
It'll be a record of a string and string
pair. Finally, we can define the next
object in the response and we'll pass in
the revalidate in case we want to
revalidate the response in a specific
amount of time. Finally, we have to
check if the response went okay. So, if
it's not an okay response, in that case,
we can extract some kind of an error
body which is going to be of a type coin
gecko error body equal to await
response.json. And then on this
response.json, JSON. We can also chain a
catch that looks like this and then
returns some information. Finally, we
can throw a new error that'll say API
error and it'll include the response
status so we know what happened as well
as
error body error or if that doesn't
exist it can also include the response
status text. So this way we for sure get
something back. And finally, if the
response is okay, we're going to return
the response.json.
So what we're doing here is creating an
abstraction that allow us to much more
easily make every single endpoint call
later throughout the application. So for
now, we want to implement the first one,
the one that's going to fetch the data
for Bitcoin, what we already got the
dummy data for. To do that, we can head
over to the Coin Gecko API reference for
the demo API. And here you can check how
to fetch the coin data by ID. This
endpoint allows you to query all the
metadata, including the images,
websites descriptions address and
market data as well, such as the price,
all-time high, exchange stickers, and
more. The only thing we have to do is
call the forward slashcoins slash ID. So
if you press the try it button right
here, you'll see how the request will
work. You have to feed it with your API
key. Thankfully, we have ours. We have
already stored it in our application. So
I'll simply copy it and paste it right
here. And then you have to pass the coin
ID. In this case, we can go with
Bitcoin. As soon as you do that, you'll
see that we get back this kind of an
information. an object containing the
ID, the symbol, the name, the web slug,
and much more information about Bitcoin,
even how it's called in different
languages. This is pretty cool, but what
I care about is the price. So, you can
see here the market data and the current
price in all the different currencies in
the world. This is amazing.
And yeah, it contains a lot of data. So,
now we want to implement it within our
code. I'll head back over here and since
we've built a fetcher, we can actually
just make a simple call directly within
our homepage, which is where we need to
actually call the data. So right here on
the home, I'll turn this page into an
asynchronous page. So we can await calls
right here. And then I'll say const coin
is equal to await.
We're going to import the fetcher that
we have created in our coin gecko
actions.
We'll define the type that we're
expecting this to return. So it's going
to be the coin details data. And what
are we doing here with TypeScript? Well,
if you remember in the fetcher function,
we defined that we're going to pass a
specific type T. And when this function
is resolved, it'll return T type. So
since fetcher will call different API
endpoints like an API endpoint for coin
details but maybe sometimes it's going
to be the entire coin list or it's going
to be market or all the different data
we cannot yet know to define it at this
time. We have to define the type at the
time when we call the fetcher and that's
in this case returning the coin details
data which is exactly what you've seen
from the coin gecko demo API directly in
their playground. So now we know that
this coin will actually contain the coin
details data which is going to be the
ID, name, symbol and so on. So if we try
to do something that doesn't exist like
coin.est, we'll immediately know that
test doesn't exist on coin data details.
But if we try to do something like
coinid, you'll see that this one is okay
and it's of a type string. That's the
beauty of TypeScript. Now the endpoint
for this one is going to be forward
slashcoins slash bitcoin
and we also need to pass an additional
options object right here of
dex_pair_mat
which I'll set to symbol. What this does
is it allows us to return back a simple
human readable token symbol like for
example BTC for bitcoin instead of the
entire address. Okay. And now that we
have done this, you see that we're
getting an error. And that's good
because that means that our fetcher
error handling is working, telling us
that we got a 401 unauthorized. And
that's being thrown directly from this
line right here in the fetcher. That
immediately makes me think that we have
an issue with our API key or in the way
that we set our API key. Yep, right
here. We should have put CG as Coin
Gecko, not CH.
And as soon as you do that, you can see
that the error is gone. And for the
request, we get back a 200. So the only
thing we have to do now is properly use
the data of the coin that we're getting
right here. So right in the header, we
can remove this image right here. At
least remove the static decoration of it
and say that the source is equal to coin
image.large.
The al tag will be set to coin.
Then for the name of the coin, we can
set it to coin.name
forward slashcoin
dos symbol.2
uppercase. And then finally the price
which is under coin domarket
data.currencore
priced.
I knew it's going to be here because I
tried it before within the coin gecko
playground. Now, as soon as you do this,
you'll see that we're going to get an
invalid source prop because Coin Gecko
images are not yet supported under our
next config. So, copy this host name and
head over to our next config, which is
right here. And then on top of the
assets Coin Gecko, go ahead and add
another one
that's going to be the host name of
coin-im images.cocko.com.
If you do that, it'll be able to
properly pull the image from Coin Gecko.
And now we can go back to our homepage.
We can also format the price in a bit of
a better way. Again, AI tools are
perfect for this. So I'll just select
it, press command shiftp, and open up
juni. And I'll tell it to create a
utility functions
within the utils file that'll properly
format
a number in a currency format and then
apply it within homepage. And I'll even
paste this part where we're using it.
Let's see how well it does. So, I need
to update a currency formatting utility.
Okay, it already edited utils.ts
and it updated the homepage, the price
cell. Oh, I can see a change right here
on the homepage immediately. And that's
it. Check this out. So, it created the
format currency function within our
utils file which is right here within
the lib. And then it imported it at the
top format currency. And then it even
used it by wrapping this price right
here. And now we have commas, the dollar
sign, and everything else. Wonderful.
Now on the same page, we also want to
fetch the trending coins. So we can
display them right here. We can do that
with the trending search list API
endpoint, which is also a part of the
demo API. It allows you to query
trending search coins, NFTts, and
categories on Coin Gecko in the latest
24 hours. So to try it, the only thing
you need is a demo API key and you get
back the data. We can implement it very
easily by making another fetcher call
right here. const trending coins is
equal to a weight. We use the same
fetcher utility function. And then we
pass the type of what we're expecting to
get back. In this case, we're expecting
to get back coins, which is going to be
an array of trending coin type. And
specifically we're making a call to
forward slash search/trending.
For the second parameter which are the
options we can pass undefined because we
don't have any parameters. But then we
have the revalidate key which we can set
to 300. Now think about what's wrong
here. What you've built is exactly how
most developers and even AI would
approach this. It's correct, readable,
and works just fine. you would now be
able to map over these coins and display
them on the homepage. But when you think
about performance, scalability, and user
experience, this pattern can cause
problems as your app grows. And let me
break down why. In modern web
development, speed isn't just about how
fast your code runs. It's about when
your users see the content. When
building apps with Nex.js GS and React
server components, it's easy to
accidentally create something known as a
waterfall, which is a common
architectural bottleneck that slows down
your application. A waterfall happens
when data requests are executed one
after another rather than
simultaneously.
In a standard asynchronous function, if
you have one await call in front of the
other, it's going to wait for it. So the
coin data is fetched first and then only
after it resolves the trending coin
starts fetching the data and then it
gives the response. If each request is a
couple hundred milliseconds then the
users will definitely notice. So what's
the solution? Well the quickest way to
break a waterfall is to trigger requests
in parallel using a promise all. This
tells JavaScript to fire off all
requests at once and then wait for the
group to complete. The advantage is that
it's significantly faster than the
waterfall as both of these requests
happen at the same time. But the
limitation is that while it's faster,
it's still a blocking operation. The
server will not stream in any HTML to
the client until the entire promise all
has resolved. If one request is slow, it
holds the entire page hostage. Not good.
So, finally, there's suspense. Suspense
is a revolutionary way of handling
asynchronous dependencies. Instead of
waiting for the data to be ready before
rendering the page, suspense allows you
to render the page in chunks. You can
deliver the static parts of your page
like your navigation, layout, sidebars
immediately while you stream in the data
that comes in as soon as it does. How
does that work? Well, you have to wrap
your data fetching component, in this
case the homepage, directly within
something known as a suspense boundary.
Then you have to provide a fallback
which is usually a skeleton like a demo
version of that page without the data.
And then you have to stream send the
data to it. So think of this as a mini
master class on streaming as well. And
let me show you how it works. Right
within our components folder, I'll
create another folder and I'll call it
home. Within home, I'll create a new
file called coin overview.tsx
and simply initialize it using rafce.
Then to it, copy and paste the fetcher
for the coin details. This is going to
be it coin and paste it right here at
the top. Of course, make this one
asynchronous as well. And also move the
part that actually uses that data.
That's going to be this div that says
coin overview. Thankfully, it has a
nicely descriptive class name. So, you
take it from here, you move it into here
and you return it as its own component.
Similarly, we want to create another
component for the trending coins part.
So, also within home, create a new
component, call it trending coins.tsx.
Run rafce and then also copy the fetcher
part that we have added within our
homepage. Const trending coins. We can
now remove these ones from the home and
simply paste them in the trending coins
at the top. Then we have to do the same
thing with the JSX. So grab the
declaration of how a table is supposed
to look like. These columns right here.
You can just copy them
and remove the dummy trending coins for
now. And then just move that part over
to the trending coins. We can add them
right here at the top below where we
fetch the coins. Don't forget to also
make this one asynchronous. And finally,
paste the part where we actually display
the trending coins. That's going to be
this one. A P tag that says trending
coins. And then a div that actually
covers them, but not including the
category section. So, just the inner
part. Once you do that, you can paste it
right here. And you'll see that our
homepage has gotten pretty empty. Oh,
and since we pasted this, we also have
to wrap it in a React fragment because
we cannot have two adjacent properties,
one next to another. And we can use this
trending points data and pass it as the
data. But you can see that my
indentation is not that good because we
did a lot of copy pasting. And for that
I think it makes sense to set up
prettier and eslint. I don't like doing
those manually. So I'll call Juny to
help me. And I'll say setup eslint
and prettier within this project.
So let's see how well it does. It's
going to open up package JSON. check
whether we have it already and
potentially create some eslint
configuration and prettier configuration
files. It can even run terminal commands
for you to check how is your
installation looking. There we go. It's
going to run the lint command and then
format it. And take a look at this. It's
going to fix some errors and maybe
you'll have to do it a couple of times.
It even removed the unused import
statements and additional stuff that
wasn't necessary. And there we go. It
did it. Now I just think I have to
enable it on save. So I will search for
eslint right here within the settings.
I'll turn on the manual configuration
and detect it directly from my project
and point to the configuration file
which is right here within the coin
pulse. And I'll repeat the same thing
with prettier. So it's going to be a
manual configuration pointing to
prettier and we will run it on save.
So now if you make some changes or
indentations or anything like that and
then you press save, it'll format it
automatically for you. Wonderful. So now
we have these trending coins right here.
But we have unfortunately cleaned up our
homepage entirely. So it's not showing
any data. But we did clean it up a bit
because now we have separated the
concerns. Each one is fetching its own
thing. It looks like we have one warning
right here within our trending coins.
Let's actually make this a div with an
id of trending dashcoins and render this
as an h4 trending coins. Now it looks
like data is complaining about the
format that we're receiving these
trending coins in. So what I'll do is
I'll say trending coins dotcoins dots
slice and we'll only get the first six
or pass an empty array in case we cannot
fetch them. And now we can actually put
them to use back within our homepage,
which is now much cleaner. It doesn't
have anything. So, first things first,
we're starting with the home grid. And
within the home grid, we'll use this
suspense thing coming from React.
Suspenses need fullbacks as I explained.
So fullback for now can simply be a
keyword of loading within a div. And
then once it loads, we want to show the
coin overview. Then we want to duplicate
this right below. And instead of
rendering the coins, now we're going to
render the trending coins.
Here we can say loading trending. And
here we can say loading overview.
And now if you save it, you'll see that
basically they load instantly. But if it
actually did take some time for them to
load, oh, there we go. You saw it for
half for like a millisecond. It said
loading something. And we can also style
this trending coins table a bit better
by giving it a header cell class name of
padding Y of three like this as well as
a body
cell class name which is going to be set
to a padding Y of two like this. So
check this out. First of all, it looks a
bit weird because here we have to
display our full chart but bear with me.
Both of these two containers render
their own data. And if you open up
inspect element, head over to network
and turn on a slower network, not even
super slow, but like fast 4G. And you do
a hard reload. Maybe we have to go a bit
slower like a slow 4G or maybe even
slower like 3G because this is super
fast. You can see that they still load
pretty much instantly even if we disable
the cache. But having this fallback of
loading is a bit weird because you can
see this flashing text, right? If I keep
repeating and then it falls into place
and this messes with browser's
cumulative shift because it breaks the
layout while it's loading. So we have to
add a proper fallback to both of these
components. I think this is another one
of those opportunities where AI tools
like Juny can really shine. I'll open it
up and we can tell it something along
the lines of create a let's call it
fallback.tsx
tsx file with coin overview fallback and
trending coins fallback skeleton UIs
that match the existing and now I'll
refer to the class names that I have in
the CSS which is going to be
hashcoin-overview
fallback and hashtrending
coins fallback
CSS rules
using data table for the trending table
layout. And then we also wanted to
update the page.tsx
to use these components as suspense
fallbacks. I think I was super precise.
Uh I think it would have been able to do
it even if I was less precise. But let's
see just how well it does. So, it's
going to get the structure of the
project. Check out the CSS and then
it'll start creating the skeletons
within the file that we told it to do it
within. There we go. It understood the
task. It's going to check the structure
immediately create a component that acts
as the fallback element. So, now it
should actually Oh, wait. It did it. So,
if I reload, check this out. I keep
pressing reload. And as it's reloading,
we can see that we have empty skeleton
containers for where the data is
supposed to be as soon as it loads. So
that nothing on the screen shifts. And
then as soon as I stop pressing my
keyboard like a maniac, we immediately
see the data stream in right where it
should be. So let's see exactly how
we've done that. Basically within
suspense we have added these fallbacks
that are basically just empty divs with
some styling that replicate the
structure but not data of the website.
And again if you like go to a super slow
network. I'm not even sure if we'll be
able to notice this given just how quick
Coin Gecko's APIs are. But if we disable
cache and go to like 3G and reload. Yep.
You can see how fast that was. And I'm
going to switch it to something like a
4G 4D images to stream in as well.
Beautiful. This is already amazing.
We're utilizing two different endpoints
from Coin Gecko API. And even though we
still have this huge empty space in the
middle, which we'll implement in the
next lesson once we add our candlestick
chart, we first want to push this over
to GitHub to do a bit of a recap of what
we've done and see if we have made any
errors on the way. So to do that, I'll
head back to the terminal and I'll run
get status to check which branch we're
on. And it looks like we are still on
the feed navigation branch, which is
fine for now. We could have used better
naming for implementing the homepage,
but for now I'll do a push. And then we
can open up a BR. So get add get
commit-m
and we're going to say implement coin
overview and trending coins UI and
functionality
using the fetcher utility function
and get push. Now back on our GitHub
repo, you'll see that the feed
navigation had recent pushes. So let's
go ahead and compare them with the main
by creating a pull request. There we go.
There's no conflicts, which we know
because we're the only ones working in
the project. But let's wait about a
minute for Code Rabbit to check whether
we have some bugs or inconsistencies. In
this lesson, we've established a
cryptocurrency dashboard foundation with
tooling configuration with ESLint and
prettier, a serverside API integration
layer for Coin Gecko data, and a
reusable table component architecture
with homepage components such as coin
overview and trending coins. We have
made the changes across all of these
different files. This was quite a
complex review because we've added a lot
of code since the initial PR. So, let's
review Code Rabbit suggestions. We have
a couple of nitpicky comments such as
the search model currently being a
placeholder and it immediately tells us
that it can create an issue to track the
implementation of the search model
feature. This is amazing. We also have
the util where we can maybe shorthand it
in coin gecko actions. Instead of catch,
we can also add a dot then. And this is
a nice one. This is a redundancy where
we use the slice or an empty array
because slice always returns an array
never null or undefined. Okay, this is
also a great way to learn new things and
how they behave. But let's take a look
at more important errors that we have
right here. The first critical one comes
from the coin overview.tsx
where it's suggesting that we add error
handling for the data fetching
operations. This async fetch operation
has no error handling and if the API
request fails the error will propagate
and crash the page render. We need to
wrap it in a try and catch block or rely
on an error boundary at a higher level.
So it suggested a plan where we simply
wrap it in a try and catch and then
return a fail div if it doesn't work. I
believe it's going to suggest the same
thing for the trending coins. This is
great. So we can actually use a prompt
that they suggested to try this out. And
back within the code, I'll just open up
Juny,
paste it in, and then I'll say also do
the same thing for trending coins
because we know that the same issue is
happening there. Now, while it's solving
the issue, we can take a look at the
other issues that we have right here.
One of which is a duplicate ID. Okay,
this is definitely an issue that we have
right here with the trending coins. So,
let's head over into trending coins. And
yeah, it looks like we have a duplicate
ID. The table itself doesn't need to be
within a div. When it comes to the
catch, we don't have to close it right
here, but rather it's enough to only
wrap this part with try and catch. And
we can extract this trending coins at
the top. So, if we do it like that,
trending coins, then redeclare them
right here, we can then use them later
on without being afraid that something
will break. And let's check out the
changes that happened right here within
the coin overview. While this is an
interesting solution, I definitely
prefer wrapping just the call itself
within a try and catch block. So I will
add it right here and end the return
regularly. Of course, we have to extract
the coin declaration above so that we
can actually use it. Perfect. With that
in mind, we just fixed a couple of very
critical issues. So with that, let's
push our changes by running get add dot
get commit-m
fix code rabbit critical issues and get
push. As soon as it recognizes the
changes, we can go ahead and merge our
PR. Perfect. The next lesson will be all
about implementing this candlestick
chart. So let's prepare ourselves for
that by opening our terminal, checking
out to the main branch to pull all the
latest changes from the main branch. And
then we can create and check out to a
new branch by running get checkout-b
feet candlestick
chart.
Perfect. That means that in the next
lesson we can start implementing it.
In this lesson, we'll create a
candlestick chart that shows how a coin
is performing over a specific period of
time. You can check out today's
performance, drag it around, and zoom it
if you need to, or maybe check out how
it performed over the last year.
This is looking amazing. You can even
increase or decrease the range as well
as the date range as well. This is
pretty cool. We'll start by installing
Trading View's lightweight charts. So
you can simply run mpm install--save
lightweight charts. In a matter of
seconds, it's going to be installed and
we can start using it. We can create a
new component within the components
folder and we can call it candlestick
chart.tsx
and run our AFCE. We'll reuse this chart
whenever we need to show some kind of a
market data. But since this is going to
be used on the client side only and has
to be rendered that way, we'll pass
server components such as the coin
overview as its children. So you get the
best of both worlds, keeping the other
data server side rendered and only
rendering the chart on the client side.
Let me show you what I mean by that.
First, I'll head over to our home coin
overview component. Within it, we want
to fetch some additional data alongside
just fetching the coin data details.
We'll use another one of Coin Gecko's
endpoints called the OLC chart. Open,
high, low, close of a coin based on a
particular coin ID.
It'll give us all the data needed to
create a chart. So, we simply need to
follow this endpoint. We can do it right
here in the try block after getting the
regular coin data. So, I'll still call
the fetcher. We'll define the type of
the data we're trying to get. OHLC data
specifically an array of those data
endpoints and then we can go to coins
Bitcoin OLC
and then we can provide an object with
additional options such as the versus
currency. We're going to do everything
in USD. The days can be set to one by
default. Interval can be set to hourly.
And we can also decide which precision
do we want to get. In this case, I'll go
with the full precision. So here we're
making another call and we're getting
back the coin OHLC data. So I'll create
a new variable for it. Let coin OHLC
data like this. And then we can set it
right here by awaiting our fetcher call.
Now keep in mind as before we're calling
these one after another but instead we
can call them in parallel as these two
pieces of data don't depend on one
another. So what we can do instead is
use promise that all. So right here I'll
say const dstructure the data such as
the coin and the coin ohlc data and call
it like this await promise.all at all
and then into it we simply need to pass
the two functions like this. This is
going to be the first one
and then the second one is going to be
this one. That way both of these are
going to be happening at the same time
and only when both are done it's going
to fill in these variables with data.
What we're doing here is declaring them.
So there's no need to declare them
above. So one thing that we can do if we
want to directly use the data is we can
put this return statement directly
within this try block. So if it's
successful go ahead and return this data
immediately
else return the coin overview fallback.
And now the question is how do we use
this candlestick chart as I told you it
has to be rendered on the client but we
want the rest of it to remain server
side rendered. So, inside of the coin
overview, we will call the candlestick
chart. And by the way, if you're
wondering why is it called a candlestick
chart, if you check out the final
version of the application and maybe
zoom in a lot, you'll see that these
seem like candles, but rather they show
you the high and the low price that that
coin has reached during this specific
period of time. For example, here each
candlestick shows one hour and you can
see that during this hour right here,
the lowest price has been here and the
highest has been here. It is red because
it is lower than the previous price
right here. So, we have this chart and
we're going to pass this part as
children to the candlestick chart. I'll
simply pass it right here. That way it's
not going to appear immediately. But
when we pass the data to it such as data
is equal to coin ohc data and we pass
the coin id in this case bitcoin. We can
head over into the candlestick chart and
implement it by giving this div an id of
candlestick chart. Within it we can
render another div that'll render the
chart header. So it'll have a class name
of chart dash header. Within it, we can
render a div with a class name set to
flex-1.
And here we're going to render the
children that we're passing over as
props. So right here we can dstructure
the children as well as the additional
data that we're passing over into the
chart. So that's going to be children,
data, the coin ID, maybe even the height
of the chart that we want to display,
which by default can be set to 360
pixels, and maybe the initial period,
which by default we can set to daily.
And this will be of a type candlestick
chart props. Perfect. So now we can
still see this overview which loads
nicely with the skeleton. But now we'll
also be able to display a clientside
render chart. So we can give this a use
client directive. First I'll start with
creating the JSX for the chart. So still
within the chart header below the
children, I'll render a div that'll have
a class name set to button group. This
one will keep track of different buttons
for the periods that we want to
manipulate our charts to show the data
for. So I'll render a span that'll say
period and we can give it a class name
set to text-sm
margin x of two to give it a bit of
margin on left and right. Font- medium
text-purp.
I found this color to work great as it's
not so much in your face. And then we
actually want to show the buttons. These
buttons are going to look something like
this. Each one is going to be a button.
It'll have a key. For example, this one
will be for 1 hour. And then it'll have
a class name whether it's active or not.
For now, I'll give it just a regular
config button class name. It also has to
have an on click. So on click will later
on have to trigger the period change.
And if we're loading the data for that
specific period, we want to set it to
disabled. So it'll be disabled if we're
currently loading. And that loading will
of course be a new state that we can
create and track right at the top of
this component. So I'll say use state
loading set loading and at the start set
to false. And within the button we can
say something like one hour. There we
go. So now we can see the button for one
hour. And we want to have multiple of
these buttons like one day, one week and
so on. But instead of duplicating it all
here, we can create a constants file.
This is a new file which we can create
in the root of our application inside of
which we'll create all sorts of
different constants that we can reuse
across the application. So I'll create a
new file called constants.ts.
And inside of it, you can export a new
constant called period buttons
like this. We typically use uppercase
letters and an underscore in between to
denote that this will not change and is
a constant. This will be an array where
each button will have a value of
something like daily and a label which
will be just one d to keep it short.
We can then duplicate this and make it
weekly as well. And here we can say 1 W.
And now we could go ahead and add more
of these. Within the constants file, you
also want to add some data for the chart
like the colors, the height, the width,
and so on. Just so we don't have to
write these by hand, I'll provide them
for you in the video kit down below.
It's constants.ds. Just copy it and
paste it right here. You'll see that we
get our period buttons. So they go from
daily all the way to yearly and then
max. And we also have our period config.
So we can track the period. Here we also
have some information for the chart such
as the background color, text, grid, and
all the colors to make it look good with
the theme of our application. Now that
we have created those buttons, we can
simply map over them by creating a new
dynamic block of code and saying period
buttons dom where we get each individual
button from which we can dstructure the
value and the label. And for each one,
we can automatically return the button
like the one I've created right here.
But instead of simply rendering 1 hour
for every single button, it'll have a
key set to the value. So it's unique and
it'll render its label. So now you can
see 1D, 1 week, 1 month, 3 months, 6
months, 1 year, and max. Amazing. We
also want to know which one is currently
active. So to be able to track that, we
can create a new function called handle
period change right here at the top.
Const handle period change is equal to a
callback function
that accepts a new period that you
select when clicking on it of a type
period and it checks if new period is
the same as the current period. In that
case we don't have to do anything. It's
already set. But else we actually want
to update it. And then here we can
update it. We'll do that later on once
we can actually visualize the chart. So
I'll say update period
and that period will of course be
another state that we can keep track of.
So I'll say use state
period set period and at the start set
to the initial period which is daily by
default. And now let's also give it an
active state. For example, by default
daily is selected. So under class names
we can say if period is triple equal to
the value that is selected in that case
we can render the config button active
else we'll render the config button. So
now we can see that one day is selected
and we cannot change it yet but we can
at least try by calling the handle
period change and passing the value that
we want to change to. If you do that and
here if you actually update the period
like set period to be equal to new
period you'll also be able to see that
we can now swap the period even though
this doesn't mean absolutely anything
yet as we can't yet see the chart. So
that is the next part that we have to
implement. Right below all of these
divs, below the final closing div, I'll
render a self-closing div within which
we're going to insert the chart. To be
able to do that, we have to give it a
reference. A ref equal to chart
container ref, which we'll define above
a class name set to the chart, as well
as a style that includes the height. So
let's create this chart container ref
just above. I'll do it at the top of the
component. const chart container ref is
equal to use ref. We can even give it a
type of HTML div element or null if it
doesn't yet exist. And we'll pass in the
null value at the start. Don't forget to
import use ref from React so we can
actually use it. And we'll also need a
couple of other refs to be able to
display the chart properly. First one is
to the DOM element that will host the
chart. But then we have a ref to store
the chart instance across renders. So
I'll create another one called const
chart ref is equal to use ref of a type
i chart API or null and at the start set
to null. This I chart API is coming
directly from the package we installed
i chart API and that's going to be a
lightweight charts. And finally we'll
need a third ref to track the instance
of the candlestick series. So I'll say
const candle series ref is equal to use
ref i series API like this. It's not
uppercased. I made a mistake right here.
It's just regular letters. That way
it'll autoimp import it for you right
here. And IER API also coming from
lightweight charts. Specifically the
type of the chart that is candle stick
like this or null.
So make sure to properly close it,
properly type it. And at the start it's
equal to null. And now we have to
refetch the data for the chart whenever
the period changes. So let's create a
new use state right here that we'll call
OLC
data. That's the data for the chart. And
set that data. The data will be of a
type OLC
data. Specifically, an array of those
data points. And it'll start as the data
that we receive through props or just an
empty array. That way we can initialize
it as the data coming through props. But
then if it changes we can actually
accommodate for those changes.
We can do that by creating a new
function const fetch oc data which is
going to be equal to an asynchronous
function that accepts a selected period
of a type period. And then we can open
up a new try and catch block. And here
we can do something similar to what
we've done when we initially fetched it
right on the homepage or rather that's
going to be within the home coin
overview
where we fetched this data right here.
So we'll simply copy the part where we
initially fetched it and paste it right
here in the try block. So what's
happening here is we first have to get
access to the selected period but
specifically we need to look into our
constants where we have the period
configuration. So we can't simply say 1
day or 1 week. We have to provide both
the days and the interval and the name
of that specific config. So that's why I
created this nice configuration object
from which we can then extract the
proper configuration structure. So we
can say const config is equal to period
config and then into it or from it we
select the right period. Once we have
the configuration we can simply fetch
the data by saying const new data is
equal to await fetcher. Same as before
this time it doesn't have to be bitcoin
rather it's going to be whichever coin
ID we're fetching the details for. So
we're going to make this a template
string. The currency versus is going to
be USD and days. Now it's going to be
dynamic. It'll depend on config. days.
And same thing right here. The interval
will be coming from config.interval.
You can shorten this a bit by
automatically dstructuring the days and
the interval from the config like this.
And then you can just shorten this part.
And if both key and the value are the
same, you can just keep the former. So
it'll look like this. Finally, if the
data changes, we can say set OHLC data
to be equal to new data or if that
doesn't exist to an empty array just so
our app doesn't break. And in the catch
block, I will console.
And say failed to fetch OHLC data and
also render the error. Wonderful. Now
whenever we try to change the period we
can refetch the data. And in this case
we can also start something known as a
transition. This is a new react hook
called use transition that transitions
the state but keeps the UI responsive
during async updates. To do that you can
do it right here. Const is pending. So
the change of the period and then
instead of set pending it gives you a
start transition function where you can
just use transition coming from react
make sure to import it and then right
here in handle period change where we
need to update the period. We can start
the transition before we actually set
the period. So I'll say start
transition.
It's going to be an async callback
function within it. And then I'll set
the period to be equal to new period.
And at the same time, we'll fetch the
data by saying await fetch OHLC data.
And we'll pass in the new period that we
want to fetch the data for. Finally, we
have to create a use effect to take that
data and to actually append it to the
chart. We can do that with, as I said, a
use effect. So I'll open up a new
callback function within it. And as the
dependency array, we'll only take the
height of the chart. So if the height
changes, we can properly reposition it.
Now the way you feed this data into the
chart is you first get the container
which is going to be the chart container
node. So you can say const chart
container ref.curren.
Then if there is no container, we simply
exit out of the use effect. Else we need
to decide whether to show time labels
based on the period. So I'll say const
show show time and then it's going to be
daily. We can also display weekly and we
can do monthly if it includes the period
that we're choosing. And with that we
are ready to create the chart by saying
const chart is equal to create chart
coming from chart API to which we pass
the container where to create it and
then some potential options such as we
can spread the get chart config which
you also have to import and into it pass
the height as well as the show time and
we can also provide the width set to the
container.client client width. So it
takes the full width of the screen. If
you reload now, you should be able to
see something that looks like a
container for a chart, but nothing
within it just yet. So that means that
now we have to add a series, a series of
candlesticks or a series of data points.
So say const series is equal to
chart.add series
candlestick
series like this. and get candlestick
config like this. I'll actually collapse
this a bit more so we can see the full
code in one line. Once we do that, we
have to convert and set the initial data
into the series. This is where it starts
getting exciting. So, type series dot
set data and into it we need to pass the
OHLC data like this. But the format that
data is within is not going to work
right off the bat. We have to make it
work specifically for the chart. So, if
you head over into our utils, like lib
utils, we need to create a special
function that'll make it work for the
chart. I actually prepared a couple of
functions that we'll use throughout the
rest of this course. So, you can find
the complete utils.ts file in the video
kit down below. Just copy it and paste
it here. One of these is going to be the
format price, which I believe we renamed
to format currency. So, make sure to fix
that. And then you'll see that the other
ones include the format percentage,
trending classes, time ago, but more
importantly the convert OHLC data. To be
honest, I created this with an AI agent
and it just makes sure to take the OLC
data that we're getting from Coin Gecko
and it properly formats it into the data
that the chart accepts. So now we can go
here and we can wrap the data with the
convert OHLC data function. Just like
this. And there we go. For the first
time in our application, we are starting
to see some real candlesticks data
chart. The period is not going to change
just yet, but the initial candlestick
chart is right here. So, what else do we
need to do now? Well, you know how if I
reload, it's not going to fit in
immediately. It's going to feel a bit
weird taking the right portion of the
screen. So what we can do is say
chart.time
scale and we can use the fit content
function to make it fit initially in the
whole screen of the chart. Now we can
store this chart instance in ref for
later updates or cleanups by saying
chartref.curren
is equal to the chart we created. And we
can do the same thing with the series.
So candle series ref.curren is equal to
the series we just created. and set up.
Now, this is an interesting thing. We
have to manually handle the observer. Uh
the observer is specifically for
resizing. So, I'll say new resize
observer that takes in the resizing
entries.
And whenever something happens, it's
going to also resize the chart. So, I'll
say if no entries.length
simply return. But if the width of the
container changes, we also have to apply
the change of the width to the chart.
Right now, if I do that, you can notice
that it says the same. This is not good
for a responsive application. So
instead, what we can do is say
chart.apply
options where we pass in the width,
which is going to be equal to entries
zero dot content rect. width. So it's
going to dynamically see the change in
the width. Now we have to use this
observer by saying observer.observe.
And what are we observing the changes
within? Well, the container that keeps
track of the chart. So if I save this
and reload, now if we try to drag and
drop, notice how it takes the full width
of the container even when it switches
to a desktop mode where it actually has
less width than it had here. Wonderful.
Finally, we have to clean everything up
at the end. So I'll say return a
callback function observer.isconnect
chart.
move. So, we're destroying the chart
instance to prevent memory leaks. And we
want to do the same thing with the refs.
Chartre ref.curren
is set to null. As well as candle series
ref, we also set it to null. And
finally, we have to make this chart work
with different periods. To do that we
have to create another use effect but
this time it has to react on the OLC
data that we provide that will be
modified depending on the period that we
click on.
So we want to check if there is no
candle series ref right now that means
the chart hasn't even been initialized
in that case we can't modify the data
but if the chart is there in that case
we can say const converted to seconds is
equal to hlc data do map where we get
each individual item of data and for
each one we return an array where we can
provide all the different data points
that a candlestick needs. First is going
to be math.f floor
of item zero divided by 1,000. The
second one is going to be the open price
of that item, so item one. Then it's
going to be the high price of the item,
item two. Then it's going to be the low
price of that specific candle. And then
it's going to be the close price of the
candle. See, even though this
candlestick chart seems like just a
regular line chart, each one of these
candlesticks contains so many more data
points. Some are necessary for the
color, some for the width and the height
of the candle and so on. So now that we
have all of these data points, I'll say
as OHLC data to make sure that
TypeScript is happy. And once we have
this data, we can say const converted
is equal to convert OHLC data
and pass in converted to seconds. Then
we're going to re-update the candle
series ref by saying candle series
ref.curren dot set data to convert the
data. And we want to update the chart to
refit the content by saying
chartref.curren
question mark. time scale and finally
dot fit content. Same as what we've done
before. Let's properly use this variable
right here. Looks like converted to
seconds is complaining a bit here.
Saying that this argument doesn't work
here. Oh, that's because I have an array
within an array. We only want to have
one array that we're returning right
here. So, map and then map.f floor and
all these things within it. Perfect.
Type script really saved us here. So, if
I reload the page now, you'll see that
it'll look good. But if I change it to 1
week, it'll actually update and give us
more data while at the same time fitting
on the screen. We can also change it to
1 month, 3 months, or 6 months. It's
really cool because you get access to
all of this data within the chart. And
even if you're in a max period, you can
still zoom in and manually get back only
to, for example, the last month and
really explore it in detail. So you can
see that changing the periods actually
works and resets the charts. It is still
very active and you can move it. And
most importantly, if you go to full
screen and reload, you can see that now
this chart actually takes more of the
screen. The only little fix that we'll
make later on is that the height of the
coin or view container including the
chart is the same as the height of the
trending coins. So that way we don't see
this empty space. We'll fix that later
on. But now we can open up our terminal
and run git status to see where we are.
So we have actually modified the coin
overview and the utils as well as added
this new candlestick chart directly on
the new branch. So now that the chart is
done, we are ready to push all of these
changes and see whether we have some
bugs. So I'll run git add dot get
commit-m
implement candle stick chart and get
push. We'll of course have to connect it
to our upstream origin. Press enter. And
now we can open up a PR directly within
our GitHub. Back within our repo, you'll
see that we can compare and open up a
pull request. So let's do just that. And
let's wait for Code Rabbit to come up
with a code review. And there we go. In
just a couple of minutes, we got our
update. There is one minor potential
issue happening in our candlestick
chart. And it says the loading state is
never updated. So we should use is
pending instead. Yep, we created this
use loading before to disable the state
of the button. So definitely the fix is
to just replace this and use is pending
instead. We can make that change in the
code easily by heading over to our
candlestick chart removing the is
loading state right here and instead
using the is pending right here on the
disabled prop of the button. There we
go. One issue fixed. Now in this case we
are adding only height as a dependency
to the use effect but it says that it
uses OLC data and period inside as well.
This means that when OHLC data changes
the chart won't reinitialize. This is
handled by the second use effect. So
this is okay. But when the period
changes show time calculations uses
stale period value. The period
dependency is needed for the time scale
visibility to update correctly. This is
also a good fix. So I'll simply go ahead
and add the period right here. There's
also another major issue in the
candlestick chart saying that there's
inconsistent time conversion between
initial mounts and updates. On initial
mount this convert to HLC data is called
without converting milliseconds to
seconds, but on data updates we convert
milliseconds to seconds before calling
it. So the proposed fix is to also apply
the same conversion in the first use
effect. So let's go ahead and copy this
part. head over into our first use
effect. And before we pass this data, we
can remove these plus and minus signs,
convert it to seconds, and then pass in
the data that is already converted.
Perfect. That's another nice save. And
finally, there is one minor one. It is
in the coin overview saying that we can
remove the redundant await inside of
promise. Yep, this is amazing. The
fetcher function already returns a
promise. So, we no longer have to add a
weight in front of it. So if I head over
to the coin overview, we can just remove
the await right here. And it looks like
there's some kind of a warning right
here saying that avoid constructing JSX
within try and catch. And that instead
we should use an error boundary. This is
coming from ESLint. And we can look into
this later on. But for now, I believe
this is more than fine because either
way we returning some code. So even if
it fails, the component will still
return the data properly. Great. So with
that in mind, we've made our changes.
I'll do get add dot get commit-m
implement fixes suggested by code rabbit
and then run get push.
With that in mind, we are ready to merge
this PR back to main. And then within
the code, we can check out back to the
main branch, which didn't yet have the
chart at this point in time. But now if
we run gitpool to pull the latest
changes, you can see the chart and
everything we just implemented.
Wonderful. In the next lesson, let's
scroll down and implement the categories
part of our application which looks like
this. It'll show us different categories
of cryptocurrencies and tokens and so
on. For example, layer 1 tokens like
Bitcoin, Ethereum and so on. Proof of
work and proof ofstake coins, coins made
in USA, stable coins and so on. So let's
display this table next. To do that, I
will first create a new branch by
running git checkout-b
top categories. So let's dive right into
it.
To build this table of all the different
categories and let me actually expand it
so you can see it a bit better will be
very similar to the approach that we
used for the trending coins. You already
know the flow. fetch the data, create
columns, and then match the styling and
layout using data table. The goal here
is to prove you can transfer the pattern
to a new data set and make it feel
consistent with the rest of the
homepage. Treat this as your chance to
work independently because you're not
just learning new tools, you're
strengthening your muscle memory by
applying what you've already built. So,
let me help you a bit. And then if at
one point you're ready to continue on
your own, that's great. First, you need
to create a new component within the
components folder. It'll also be within
the home folder and you can call it
categories.tsx.
You can use rafce and very easily
imported within our app. That's going to
be right here. So instead of just a text
that says categories, you can now render
the actual categories component. Great.
So back within our application, you
should be able to see a little
categories right here. And don't forget
that you can also wrap it in suspense.
So right here, create a suspense with a
fallback saying something like let's do
a P tag that says loading categories dot
dot dot. And then within it, you render
the categories component. And now the
goal is to develop it. Head back over to
the Coin Gecko API, specifically the API
reference for the demo version. And here
you can find the categories. You can
just search for it categories right
here. And specifically, we want to get
the coin categories list with market
data. It's this endpoint right here.
This endpoint allows you to query all
the coin categories with market data,
market cap, volume, and so on. You can
even try it to be able to see what kind
of data you're getting back.
Understanding what data you're working
with is critical. So at the top, we can
simply fetch the categories
by making this an asynchronous function
and then using an await on our fetcher
call. Thankfully, we have created that
fetcher before. So, it's going to make
it easier for us to fetch the data.
Specifically, we're expecting to get
back an array of different categories.
And we can get it under the URL of
for/coins/categories.
Exactly what we've read in the docs. And
now we have to structure that data in
terms of columns, headers, and rows that
we're going to display in the table.
const columns is equal to it'll be of a
type data table column specifically of a
type category like this. And then we're
going to define that as an array where
we're going to have a header that's
going to have the name of category, a
cell class name of category cell. and
finally a cell itself within which we're
going to get a category and we'll return
the category name. So that is the first
column that we have. Now we can try to
display that table below by giving this
div an ID of categories.
A class name of custom scroll bar so we
can scroll through it if we are on a
smaller device. I'll render an H4 within
it that'll say top categories and
thankfully we can reuse the data table
component we created before. To it we
have to pass the columns which now we
thankfully have. We can also pass the
data which we also have. So we'll take
the categories and slice only the first
10 out of it. And we no longer have to
say give me the 10 or an empty array
because you know that code rabbit told
us that whenever you use slice if
something goes wrong it'll return an
empty array for sure. And then for the
row key we can get the index out of it
and just return it like this.
Finally close the data table.
Let's actually make this take multiple
lines so it's a bit easier to see what
we have. We have columns, data, row key,
and finally the table class name of a
margin top of three. So if you save this
now, you'll be able to see that we have
what seems to be an early version of the
table. Top categories, the first column
that displays the category names. But
now we have to add other columns to the
columns list, columns for well, let's
see, what do we have? the icons of the
top gainers, the 24-hour change, the
market cap, and the volume. I would
actually want you to try to implement
this on your own. You'll know that each
one of these columns has to have the
following things. The header, the cell
class name, which I've already prepared
for you. So, it's going to be the top
gainers cell in this case because this
one will be all about top gainers. That
is the second field that we're working
on. And then finally and most
importantly, what will we display in
that cell? You get all the data needed
to display it right here within the
first param which is category and then
from it you can actually extract the
category image. So what you can do is
console log the category, see what data
you get back from it or directly refer
to the API and then see that right here
you have something known as top three
coins and that gives you their images.
So this is exactly what we need. So it's
all about reading what the API gives you
and putting it to use. So just to give
you an example, it'll be category
dot top three coins dot map where get
each individual coin and for each coin
you render an image that has a source of
coin, an alt of coin as well. It'll have
a key of coin because it's unique, a
width of about 28, and a height of 28 as
well. So now if you save this and go
back, you'll be able to see these top
three gainers. But in this case, it
looks like the styles are not getting
applied because the top gainer cell
should basically apply a flex container
on it, which should make them appear one
next to another like within the final
application. But here they're one on top
of another. So that might mean that we
haven't properly passed the cell class
name into the data table. So back within
the data table, we have to make sure
that we have the table class name. Then
we have a header row class name. But
there are some other class names that we
have to provide. 1 2 3 4 5 six. So let's
actually copy all six of these and make
sure that we have them right here as
props.
I'll also add commas and indented
properly. Yep, it looks like we're
missing the header cell class name and
the body cell class name. So let's
provide these where they need to be. The
header cell class name will be in the
table head right here where we have the
class name. We're applying regular
classes. And then right after the
regulars, I'll apply the header cell
class name as well as if maybe it comes
from the column. So column.head
class name. And I'll do the similar
thing right here for the columns where
we have our last BR5. And there we'll
apply the body cell class name as well
as the column dot cell class name if
it's coming from the column. And now we
can see that we're applying those styles
that we are specifying right here in the
column. And now there's three more
additional columns that we have to
implement. We have the market cap and
the 24-hour volume as well as the 24hour
change. Let's do the market cap and the
24-hour volume first. They're pretty
simple once you understand where the
data is coming from. So, we need a
header for the market cap. The cell
class name will be market cap cell.
And then the cell itself will simply get
the data about the category. And we'll
call the format price or in this case
format currency and pass in the
category.arket
cap into it. Just like that, we'll have
to import the format currency from
utils. And you can see the market cap.
Now, we can basically duplicate this
below. And instead of market cap, we'll
say 24hour volume.
It'll be a volume cell. And here we can
basically provide a volume_24.
So here you can see the full amount of
this coin in circulation. And here you
can see how much it circulated over the
last 24 hours. Now, doing the 24-hour
change will be a bit trickier, but not
too tricky. Again, we're going to start
with the header. That's going to say
24hour change. Then you define the class
name, which is going to be change header
cell. And finally, you need to define
the cell itself, which takes the
category and then from it extracts the
market cap change in 24 hours. If you do
that, you'll be able to see this number,
but we have to turn it into a percentage
point and maybe a display an icon that
goes up or down depending on what it is.
We've already done something similar for
the 24-hour change in trending coins.
So, if we head over into trending coins
and notice this part where we have there
we go. This is it the price change. We
basically want to copy what we have
here. So, I'll basically copy the entire
cell right here and just override this
one that we created. Instead of the coin
here, we're getting the category,
but more or less everything should
remain the same. From the category,
we're checking whether it is trending up
or down. And we can get that based off
of the category market cap change in the
last 24 hours. If it's greater than
zero, then it's trending up. Then we
create a div with a CN of change cell.
And if it is trending up, we do a text
green of 500. else we do a text red of
500. Finally, we display the icon as
well as the actual market cap change,
but we're going to do it in a bit of a
cleaner way. At the bottom, I'll say
format percentage
and pass in the category dot market cap
change in the last 24 hours. This format
percentage has to be imported from
utils. And here you can see all the
numbers. It seems like everything is in
the negative today across all the
markets. And now if you expand this,
you'll be able to see your full table.
Maybe we can make this 24-hour change
arrow appear on the left or right of the
number. We can do that by putting the
price right here at the top and then
giving this P tag a display of flex. So
do a class name of flex. There we go.
You can see it here. And we can make it
even by adding items center. And if you
want to, we can do this for the trending
coins as well. So I'll head over into
the trending coins and simply modify
this P tag to include the class name
just like this. And here now that we
have the function for the formatting
percentages, we can use that instead of
this math. So I'll simply say format
percentage,
which of course we need to import. And
then we don't have to use the two fixed
and everything else. And we can shift it
up. This is looking great. So now we not
only have the trending coins, but we
also have the top categories. So you can
get a general overview of what's
happening in the crypto markets. Now, if
you've been paying attention, there is
one thing that you might notice, and
that is that for the top categories, we
still haven't added a skeleton loader.
So if you reload hard enough, you'll see
that these ones will have the skeleton
loader. So it seems like they load
instantly, but here you can still see
loading categories. So this is another
perfect opportunity to use Juny to
generate a skeleton for us. Then we can
then use directly within a suspense
fallback. So to be able to do that, we
can open up Juny by pressing command
shiftp or control shiftp and type juny.
And here we can just open up our chat
that we had. And we can now continue
talking to it and say do the same thing
for
and now we want to do it for the
categories and press enter. I believe it
should be able to figure out the context
of what we were discussing and just
apply the same changes that it did
before. Let's see how well it does it.
Yep, it's analyzed it and it created a
fallback and now it should update it
right here in real time as we're
speaking. There we go. Editing the
categories and there it is, the
categories fallback which seems to be
like a regular table but without the
data. Perfect. Let's go ahead and check
it out in the browser. If I now reload,
you can see that as I reload, everything
stays the same and there is an empty
table that is just waiting for the data
to pop up. Wonderful. The category table
is good, but I don't think it deserves
its own PR that's ready to be checked.
What we can do instead is also focus on
implementing all coins page. Right now,
it's a 404, but in the final version of
the application, it's going to be
another table that'll display the most
popular coins. And we can also implement
the pagionation. So, you can actually
move through this table. Since this is
going to be our third table in the row,
this is going to be a big challenge that
I have for you. More about that in the
next lesson.
Okay, the coins page, our third table.
Is it a bit repetitive? Well, yes and
no. Some columns are going to be
similar. Sure, we have change, market
cap, and volume similar to what we have
here. But at the same time, this table
will show hundreds of thousands of coins
with proper pagionation. By building
this table, you'll learn how to handle
loads of data without crashing the front
end of your application. So, let's start
doing this together. And if at any point
you feel comfortable enough to continue
working on this table on your own based
off of what we've done so far, I would
highly recommend that you try doing
that. We have to create a new route. So,
right within the app folder, we can
create a new folder called coins. And
then within coins, you can create a new
page.
That's going to be page.tsx and use
rafce to generate it. If you've done
that, when you head over to all coins,
no longer should there be a four or
four, but rather it should just say
page, specifically a coins page. And as
I said, this one I want you to do on
your own, at least before we get to the
pagionation part. There's a single coin
geko endpoint that you can use. This
time it's also coming from the API demo
reference and it is under coins coins
list with market data. You can get it by
going to coins market. And here we can
see the data that you're getting back
from it. Your task right now is to
display the table for the first let's
say 10 coins without pagionation. So try
to put into practice what you've learned
so far by reusing this data table and do
the same thing that we have done right
here within the categories where you
first fetch the data not for categories
but for the market list then display the
columns and then finally feed it all
into the table. So, pause the video and
I'll provide you with a solution in
three, two, one. And here it is. You can
ignore these couple of lines. We'll put
that to use later on once we implement
the pagionation. But for now, what you
need to do is similar to what you can
see on the screen. And if you weren't
able to fully do it, I'll provide this
file which is going to be the V1 of the
all coins page without pagionation so
far within the video kit link down
below. So you can copy follow along and
then we can continue with adding the
pagionation. So first things first,
we're fetching the coin data. We use the
fetcher function. We fetch the coin
market data array from coins markets in
USD. You can order it if you want to.
Later on we're going to add this
pigenation as I said and then price
change percentage we can leave it to 24
hours. Once you have the data we need to
form the columns. Columns for the rank
it's number 1 2 3 and so on. Then for
the token image and the symbol after
that we have the price. We have the
24-hour change as before and finally the
market cap. Once you have all the
columns, you simply feed it into the
data and that is it. Let me know in the
comments down below how well we're able
to replicate the all coins table from
the design of the application. But now
together let's implement pagionation.
For that we'll use shad cn pagionation.
It is simple yet does everything we
needed to do. So let's go ahead and
install it by copying this command right
here. MPX chat CNN add latest add
pagenation
then we can also copy its usage first
all of the imports which I'll add right
at the top first we need to copy the
imports and instead of pasting them here
I'll instead create a new component
it'll be called coins pagionation so
it's going to be a new file in the
components folder called coins
paginationtsx
where you can run rafce and then simply
import all the imports and then also
paste the usage right here and then you
can import it within the coins data
right here below the data table
that'll be coins pagionation like this.
If you now go back you'll be able to see
the demo pagionation which for now
doesn't do anything.
to be able to make it work, we have to
pass some props into it. So right here
at the top, I'll say const has more
pages to be able to figure out whether a
new page exists. For that, we need to
get the coins data.length
and figure out if that is the equal to
per page. So if at one point we get a
page that has less than 10 elements,
that must mean that there aren't new
pages. Then we also need to get the
estimated total number of pages. This is
done for smart pagionation that expands
as users navigate further. So we're
going to start at 100 pages, add 100
more when users reach page 100, 200,
etc. So I'll say const estimated
total pages is equal to and what we can
do here is get access to the current
page that we're getting from the search
params right here at the top. And if
it's greater than or equal to 100, we're
going to use the math. Seal function to
get the current page and divide it by
100.
multiply it by 100 plus 100 pages to
that and then divide it by 100. It's a
bit of crazy math, I know. So, this is
the perfect opportunity to use Juni to
explain this to us. So, I'll say explain
this line of code and what it does
simply and quickly. At some point when
you're watching a specific tutorial, you
can use these chat AI agents not only
for productivity but also for better
understanding of the code that you're
working on or the code that I'm showing
to you because I always try to explain
as much as possible but hey I cannot
directly answer the questions that you
might have. So thankfully Juny can do
that. This line of code dynamically
calculates an estimated number of total
pages for pagionation based on the
user's current position. Here's the
breakdown. If a current page is less
than 100, simply returns 100. Acts as a
default minimum. If current page is
higher than 100, then it rounds the
current page up to the nearest 100. So
if you're in 120, it's going to round it
200 and then add 100 on top of it as a
buffer. And that ensures the pagionation
UI always shows more pages without
needing to know the total amount coming
from the API which in this case the API
doesn't provide.
Basically it's a sliding window
function. So now if we fix that we can
now pass this into the pagionation.
We'll pass in the current page set to
the current page as well as the total
number of pages. So total pages pass in
the estimated total pages and finally
has more pages equal to has more pages
which we have right here. Now we have to
dive into the coins pagionation accept
all of these params and make it actually
work. So right here I will dstructure
the current page the total number of
pages
and has more pages
and these will be of a type pagionation
here to pagenate we'll use nextgs's
router functionalities. So I'll say
const router is equal to use router
coming from next navigation not next
router. So be careful here. Since we're
using the router, that means that we're
using the browser's client side
functionalities. So we have to turn the
use client directive on. Then how will
we actually pageionate? Well, we will
pageionate by changing the URL of our
website. So const handle page change.
It's going to take in the page which is
a number and it'll simply call the
router functionality and push to the
corresponding page. So forward/coins
with a parameter of page set to the page
that the user wants to pageionate to.
Then we want to get access to the page
numbers which is going to be equal to
build page numbers and then we pass in
the current page and the total number of
pages.
This build page numbers is a function
that I created before and basically at
any point in time on whichever page
we're on, it's going to give us a couple
of pages before and after it. So we can
very easily pagionate. I'll show you how
it works in practice in just a second.
And finally, we need to know whether
we're on the last page. So if is last
page
and that's going to be the case if we
have no more pages or if the current
page is equal to the total number of
pages that there are. Finally, let's put
all of those variables to use by giving
this pagionation an ID set to the coins
pagenation.
Under the pagionation content, we can
give it a class name of
pagionation-content.
And now we have the first pagionation
item which is going to be a previous
item. So give it a pagionation control
class name as well as the prev class
name. And I'll divide it a bit so you
can see what's happening. So we have
pagionation previous pagionation link
pagionation ellipses and pagionation
next. For the previous to work we have
to give it an on click. So here I'll say
on click and what we want to do is take
the current page and check whether the
current page is greater than one. If
that is the case we can call the handle
page change and pass the current page
minus one. It is as simple as that. And
we can also give it a class name and
check whether the current page is set to
one right now. If it is, then we need to
show a control disabled class name. Else
we need to show a control button that's
going to look like this.
So now you can see since we're on page
one, it is disabled. And we can do
something very similar for the
pagionation controls for the next page.
So I will simply copy the class name,
remove this ellipsus part and now into
the pagionation item that is wrapping
the pagionation next we can add it but
simply say next and then I will copy the
on click and the class name
functionalities for pagionation previous
and paste them into pagionation next.
This time instead of checking whether
we're on the first page we'll check
whether we're not on the last page. So
if not last page then current page + one
instead of minus one. And for the class
name if it is last page then we have to
disable it.
Perfect. And now finally we have to
display all of the pages in between
these buttons. Currently there's just
one. So let's fix that. I'll wrap this
in a div
that'll have a class name set to
pagionation dash pages.
Within these pagenation pages, we'll
want to map over the page numbers. So
take the page numbers and map over them
where we get each individual page and
the index for that page.
Then for each one of these pages, we
want to display a pagionation item. So
simply drag it right here. So now you
can see many ones. But let's modify it.
Since we're mapping over these page
numbers, we have to give it a key equal
to page. And then we have to figure out
whether we're displaying the actual
pageionation link to the page or
ellipses to show some space in between
the pages. So I'll check if page is
triple equal to ellipses. In that case,
we want to simply display a span element
that will have a class name set to
ellipsis and it'll say dot dot dot.
That's what an ellipsus is. But if that
is not the case, then we want to display
a pagionation link. So here we can see
that we have some pages, then some
ellipses, and then another page. Of
course, these are not all ones. They're
the pages that we want to pageionate to
that we're mapping over. So hopefully
now it finally clicked. You're currently
on page one. You can go to two. You can
see that there are 98 more pages in
between. And then you can immediately
skip to page 100 if you want to. So for
this pagionation link, let's give it an
on click
set to a callback function where we can
handle the page change and pass in the
page we want to go to. Then I'll also
give it the class name set to CN and
first pass the page link which is always
going to be there. But then we're going
to only give it a page link active class
if the current page is triple equal to
the page that we're on. So now we can
see page one is active. Perfect. And I
think this is it. Like if we now click
on page two, it should actually navigate
us and you should be able to see new
elements popping up on your homepage
from rank 11 to rank 20. Page two. We
can go to page three or you can also use
these buttons on left and right to
simply pageionate between these pages.
Oh, looks like I got an issue with the
pagionation item where the key is page.
So instead we can use the key of index
instead of the page because sometimes
there can be multiple ellipses in
between. If I continue going up, you'll
see this. And if I for example go to
page 100, you can see that now there are
more pages in between. So you can
immediately jump to 200 and so on. The
deeper you go, the more abstract and
obscure the coin names and images get.
So we'll stick to the page one for now.
But yeah, I think this is working
incredibly well. We now have our all
coins table where you can go over all
the different coins available on Coin
Gecko's APIs. The next big part will of
course be creating the coin details page
for each one of these coins. So when you
click on it, you'll be able to see all
sorts of different details such as the
price. You'll be able to check it out in
all sorts of different currencies, the
coin details, a more detailed trend over
your chart where you can update the
frequency in seconds, and the price also
updates in real time. You'll be able to
see the recent trades, the exchange
listings, the top gainers and losers,
and so much more. But before we get to
that, let's actually commit our changes
by running git add dot git commit-m
and
say implement categories
and all coins tables and get push.
Of course, we'll need to push it over to
the remote repo and we'll be able to
open up a PR and check whether maybe
some bugs sneaked in. So, let's give
Code Rabbit a second and I'll be right
back. And here's the overview. We've
added a dedicated coins page with a
pageionated listing displaying the rank,
token information, price, 24-hour price
changes, and the market cap. Also, the
category section.
So, let's go ahead and see whether we
have some issues. Looks like there is
one in the coins page where there's an
empty link that has no visible or
clickable content. This is referring to
the rank. So if we head back over here
and yeah, you'll see that the rank
itself is definitely not clickable, but
the whole row is. So that's totally
fine. Like if you click right here,
it'll actually lead us to that coins
details page. Or if you click here,
doesn't matter. The whole row is
clickable. So we could actually remove
this link because it's not necessary.
But thankfully, the link is coming from
somewhere else. Oh, this one is
interesting. It says that there's an
edge case. If it's 0% change, then it
displays as negative.
Rather, a neutral change might warrant a
neutral color or no special coloring.
This is a good minor suggestion. So, we
need to explicitly check whether it's
trending up or down. If you want to add
this change, definitely go for it. And
finally, the last one is also thankfully
minor where it says that the alt
attribute is set to the coin URL, which
is not descriptive. Yep, I agree. here.
We definitely could have used the coin
name, but for now, since it's just the L
tag, we'll leave it as it is, and we'll
get back to it later on. So, let's go
ahead and merge this PR, knowing that we
have no major bugs or issues. We can go
back to the code, check out over to the
main branch by running git checkout
main. Open up our code on all coins on
the main. And then as soon as you run
gitpool, you'll be able to see what we
just implemented. Now, over the next
couple of lessons, we'll be diving deep
into implementing the coin details page.
And this is not going to be just a
regular coin details page. I'll tell you
more about it in the upcoming lessons.
But for now, let's at least create a new
branch for it. Get checkout-b
feet
coin details.
And we can get started implementing it.
In the next couple of lessons, we'll
implement the coin details page. But
this is not just a regular page where we
display the additional details that you
weren't able to see on the homepage.
Sure, it does that, but it does it in
such a powerful way. If you pay close
attention to this price right here,
you'll notice that as I'm speaking, the
price will change in real time, and so
will this graph right here. We'll do it
by using Coin Gecko's websocket API. The
goal is to stream in the price, the
trades, the candlestick updates, all of
that in real time so that the UI feels
alive without pulling every few seconds.
I'm talking about the websocket API. If
speed of the data matters in any field,
it is of course in cryptocurrencies
because the price can change so often.
So the coin gecko websocket API provides
a persistent connection for realtime
data streaming. Ensure you get updates
immediately. What is important to
mention here that we won't be pulling
the data every minute or every couple of
seconds rather it'll really be instant
and the way in which we'll implement it
is going to be unique as well. I'll
teach you how to create a custom hook.
We can do it right away. It's going to
be right here within a new folder on our
application called hooks. And then
within it we can create a new file
called use coin gecko webocket.ts
file. This hook will act as a single
place that manages the socket
connection. It'll be responsible for
opening the socket, subscribing to the
right channels, handling pings, and
translating incoming messages into React
state. That means that every component
that'll need the live data just consumes
the hook and never deals with the raw
websocket events. Coin Gecko's websocket
API works by opening one connection and
then sending JSON commands to subscribe
or unsubscribe from channels. In this
case, we'll be listening to three
different channels. One will be the CG
simple price where we can receive
real-time updates for tokens as seen on
coin.com. The other one will be the
onchain trade which will allow us to
receive recent trades made on that token
and finally the onchain OLCV that'll
give us the updates that we can then
present within our chart. Each channel
will send a specific payload and then
the hook will map those payloads into
price trades and the data for the chart.
So let's implement it. We'll start as we
start with creating any other custom
hook and that is by creating well a hook
export const use that is the keyword
that makes this function well a hook. So
we can say use coin gecko
web socket and it'll accept a couple of
props like here for example we'll need
to pass in the coin ID which is going to
be the coin identifier to subscribe to.
Then we have to provide the pool ID
which is going to be the onchain
identifier used for trade and candle
streams. And finally we'll need the live
interval as well to define how often we
want to see the changes. And these are
going to be of a type use coin gecko
websocket props. And we can also define
the return. So right after the props we
can say use coin gecko websocket return.
So we know what this hook is returning.
And then we can open up a function block
that's going to look like this. Within
here, we'll first need a reference. So
I'll say cons WS, which is short for
websocket. Ref is equal to use ref,
which we can import from React. The type
of this will be webocket or null because
we're going to start this as a null
reference. After that, I'll say const
subscribed, which is going to be another
ref. And this one will track the active
channel subscriptions to prevent
duplications. So use ref. It'll be a set
of strings like this at the start set to
a new set. So let me properly close
this. There we go. After that we can
create the local state to track the
latest price snapshot from the socket so
we can display it. So for this we'll
need a use state. It'll be called price
set price
and at the start it'll be set to null.
The type of this price will either be we
can set it to the extended price data or
null since it's null at the start. So
let's properly set it to the null value
at the start. There we go. After that,
we also need a local state for rolling
list of recent trades. So I'll create
another use state and we'll call it
trades. And this will be set to the type
of trade array like this. And at the
start, it can be set to an empty array.
Finally, as I showed you that we'll be
using three different endpoints. We'll
also need three different use states to
manage the data. The last one will be
OLCV
for the candlestick chart data. And this
will be set to the OLC
data or null. Another start set to null.
Finally, we'll need one last use state
to denote whether our websocket is
ready. So, is WS ready? And then at the
start, we'll simply set it to false.
Now, I'll open up a new use effect. And
within this use effect, we'll have to
connect to our websocket. It'll have an
empty dependency array. So, it only
executes at the start. So, how do we
actually connect? Well, we'll need
something known as a websocket base,
which I will define right here at the
top. const
w_base
is equal to I'll turn it into a template
string of process.env.next_pub_coin
gecko webocket
URL. If you head over to your process
envo,
you'll see that we don't have an ENV
like that. You'll see that we don't yet
have that ENV, but you can find it very
easily by heading over to the Coin Gecko
documentation and then at the top of any
one of these endpoints that we're going
to call, you can see the URL. So, simply
copy it and add it right here as next
public coin gecko websocket URL. So,
it's wss and then stream.cocko.com
atcoin.com v1. You can find it directly
on the docs. We also need to append our
key. So I'll do a question mark x cg_pro
api key is equal to and now we'll have
to pass our process.v
next public coin gecko API key. And if
you take a look, we already have that
key right here. Now there is one
important distinction to be made right
here. If you head over to the websocket
beta API page, if you scroll down,
you'll see that Coin Gecko websocket is
available for paid plan customers,
specifically analyst plan and above. So
to continue following along,
specifically receiving the realtime
updates through a websocket, you'll have
to go to the API plans and upgrade over
to analyst.
That way, you're getting all of these
websocket functionalities. So, if you
decide to get it, you can continue
following along with me implementing
websockets, but if not, that's totally
okay. You can check out the API
reference for the demo API and then
explore the endpoints right here. I'm
leaving this as a challenge to you. You
should be able to implement most if not
all of the functionalities of the coin
details page, but the thing is they
won't update in real time. So with that
in mind, let's continue connecting to
our websocket right here. Right within
the use effect, I'll say const WS is
equal to new websocket to which we have
to pass the websocket base. Then I'll
attach it to our reference by saying
wsref.curren
is set to the websocket we just
initialized. Then I'll create a small
helper function to help us send JSON
payloads. Const send is equal to a
callback function that accepts a payload
which is going to be of a type record of
string and unknown and it'll simply call
the websocket
send function and it'll stringify the
data before we send it wrapping the
payload. So it'll basically serialize
payloads which is a part of the server
protocol. Then I'll create another
helper called handle message which is
going to handle every incoming message
from the socket. So that's going to be
equal to an event that comes in of a
type message event. Then within it we'll
get access to a message of a type web
socket message which is going to be
equal to the parsed event data. If the
type of the message is ping, in that
case, we can use this send functionality
to send a type of pong and exit out the
function. These are what's known as
server keep alive ping messages. As long
as we're responding, we're good. We're
keeping the connection alive. Now, we
want to create a message that will
receive once we successfully subscribe
to specific updates. So, I'll say if
message.type that type is set to confirm
subscription.
In that case, we want to extract the
channel name from the identifier by
saying const channel like this.
And we'll get it by parsing the message.
So JSON.parse
message question mark.identifier
or an empty string if it doesn't exist.
And this will still be a part of the
handle message functionality right here.
Then we want to record that this channel
is now subscribed. So subscribed.curren
add channel. Perfect. Now let's also set
up the messages for the price updates.
These matter the most. So I'll collapse
these two ifs. And you can see that
we're essentially creating different
sorts of function blocks for handling
different sorts of messages. So if
message C is triple equal to C1 then
this means that this is all about the
price. So here we can call our set price
state and we'll have to manually set up
all the key value pairs and the C1 is
just a string. So first things first the
USD price this is going to be message.p
USD price falling back to zero if
something is missing. So we can do it
like this. After that we'll have the
coin which is going to be the coin
identifier. So this is message.
Then the price itself the raw price
value from the payload is going to be
message.p.
Then the change 24 hours is going to be
message.ppp.
Then we'll get the market cap which is
going to be message.m this is the market
capitalization value. The volume 24
hours is going to be message.v V which
is the trading volume and then finally
the time stamp of when it happened which
is going to be message.t
and with that we are successfully
updating the the price. One part of the
updates is the price and there are two
more right another one is for the trade
updates. So I'll say if message
C is triple equal to G2 then here we're
getting all the trade updates. I'll form
it into a new object. New trade of a
type trade is equal to an object where
we're getting the price which is
message.pu.
Then we get the value which is
message.vo
value over volume.
Then we get the time stamp which is
going to be the message.t
which we can set to zero if it doesn't
exist. Type is going to be message.ty TY
which is going to be buy sell or trade
indicator and finally we have the amount
which is message.2. If you're wondering
how I knew that these values would be
under these shortened keys that is
coming directly from the docs. So if you
head over to the in this case we're
using let's see CG simple price you
should be able to see the data right
here. There we go. So this is the data
payload. And for the last one we'll be
dealing with the OLCV data. So if you
come here and scroll down, you'll be
able to see exactly what we need to get
out of it.
Okay. So once we get the trades, we
actually want to get an array of those
trades. So we can simply call set trades
to set our state. We get the previous
axis of the trades, but then we set the
new array. So we first add a new trade
at the top. Then we spread all the
previous trades and then we slice it
from 0 to 7 to have a max number of
trades.
And now finally let's deal with the most
important part which is the data for the
chart. We'll get that data if message
ch is triple equal to G3. This is the
channel type. In that case, we can get
access to the time stamp which is coming
from message.t.
We can see that right here. And if it
doesn't exist, I'll set it to zero. Then
we want to build the candle in the
expected order. So say const candle
again that is one piece of data on the
chart that is actually combined of
multiple pieces of data. So this is of a
type OHLC data and it needs to contain a
time stamp for that data. And then after
the time stamp, I'll provide message.
Oo, which is going to be the open price.
Message
H the high price, message.L, which is
going to be the low price, and finally
message. C, which is going to be the
close price. You can see for these
values, it says that type number or
undefined is not assignable to type
number. So if it doesn't exist, we can
just let Typescript know that it can
maybe take a value of zero
like this. And we can wrap all of them
within a number converter. So we know
that it's going to be a number for a
fact like this. That way we're just
getting much more expectable data.
Finally, now that we have formed this
handle, we want to add it to set ocv
data and we can simply pass in the
candle. Wonderful. So now we can exit
this handle message part because we're
handling all sorts of different
messages. And we can mark that the
socket is ready when the connection
opens. So WS.onopen
is equal to a callback function
where we set is WS ready to true. Then
we attach the message handler to
incoming events by saying WS on message
is equal to handle message. This is what
we worked so hard to create right now to
handle all sorts of different messages.
We want to mark socket not ready when
the connection closes. So that is WS.on
close. We'll say set is WS ready false.
And finally, we want to close the socket
when the component using this hook
unmounts. So I'll say return ws.clo.
Perfect. So this use effect is managing
the connection to our socket and it's
analyzing its responses and giving them
to us in a nice meaningful states in
React. And now we want to subscribe to
this connection that we created. For
that, I'll create another use effect
that'll manage the subscriptions. This
one will accept a couple of different
props in the dependency array. The coin
ID, so whenever it changes, we know that
we can change the subscription, the pool
ID as well as the is WS ready and the
live interval. Whenever these change, we
also want to recall this use effect.
Okay, so with that in mind, first things
first is that we got to check whether
our websocket is ready. That's what
we've done in the previous use effect.
So if not, is WS ready? We simply return
or exit out of the function. Then if
it's ready, we can actually create a new
instance. So WS is equal to
WSREF.curren.
And then if no WS, we also return. But
at this point, it really should be here.
And then I'll copy the helper function
that allows us to send these messages
from the previous use effect and paste
it right here. And now we need to create
different functions. Functions for
unsubscribing and subscribing to
different streams. First I'll do
unsubscribe
all. So let's do unsubscribe all is
equal to it's not an asynchronous
function.
And we simply have to say
subscribed.curren
dot for each channel.
We simply want to send a new message
with a command of unsubscribe
and an identifier
of JSON.stringify
and then pass in the channel within an
object just like this. Finally, once we
are done with unsubscribing for each
channel, we can say
subscribed.curren.clear
to clear all the subscriptions.
Perfect. So now that we have the
function to unsubscribe from all to give
us a nice starting point, we can also
create a new function to subscribe to a
specific channel. So right here, I'll
say const subscribe.
It's going to be equal to a regular
function. But here we have to have some
props to know what to subscribe to. So
we'll accept a prop of channel of a type
string as well as the data which is
going to be of a type record of string
and unknown. Then within it if
subscribed current has this specific
channel we simply exit because we're
already subscribed.
Else we want to send the command to
subscribe. So I'll say command of
subscribe with an identifier of
JSON.stringify
and we simply pass in the channel. While
we're working on this, I'll actually
make this full screen so we can see all
of the lines a bit better. Then if we
also have some data, we can also send
another request this time with a command
of message.
We also need to pass the identifier to
the channel. But now we can also pass
the data of JSON stringify data. And
this is our function to subscribe to
different channels. Now we can invoke a
new method called Q microtask. And here
we can provide a callback function where
we can reset the local state after the
current call stack to avoid mid render
updates. We want to start by clearing
the price. So set price to null so that
the UI resets when subscriptions change.
Then we want to set the trades to be
equal to an empty array. We're doing
this so the list restarts for a new
pool. And we also want to set the OHLCV
data to null. So the chart resets for
the new pool. And finally, we want to
remove any existing channel
subscriptions before subscribing to new
ones. So here we'll call the unsubscribe
all function and then we'll subscribe to
the price updates for the selected coin.
So we'll use the subscribe to CG simple
price by passing the coin ID. So that's
going to be coin ID of an array coin ID
with the action of set underscore
tokens. So this is it for queuing those
microtasks at the start. Then we want to
be able to get all of the recent trades.
This is useful for displaying the recent
trades in real time right here in the
recent trades table. For example, if we
go to a coin that has a bit more new
information, like let's see, maybe zero
base. Yep, looks like here we'll be
getting more new trades. Or maybe this
Audierra coin, which seems to have many
more recent trades that, as you can see,
are flowing in in real time. This is
super cool to see. So for the recent
trades what we can do is we can exit out
of the Q microtask. We can set the pool
address by getting it the pool ID and
replacing the underscores with a colon
sign. And then if a pool address exists
then we want to subscribe to it. And we
can do that by saying subscribe pass in
the onchain but make sure to spell it
properly onchain trade and then
additional options such as the network
underscore id pool
addresses
and set that to an array of pool address
and finally provide an action which is
the command to set the pool filter on
the channel. So it's going to be set
underscore pools.
This is it for subscribing to the
onchain trades. And now just below it
want to do the same thing to subscribe
to the candlestick chart data. So I will
duplicate this below. I'll subscribe to
the onchain
OLCV. We'll leave the network ID
addresses the same. The action will be
the same. But here we'll pass an
additional piece of info which is
interval which we'll simply set to the
live interval that we have defined
through props. Perfect. So what we have
essentially done right here is created
one use effect that sets up our
websocket and make sure that it is
alive. Then we have created another one
where we actually connect that websocket
and subscribe it to all of these
different channels for onchain trades
onchain candlestick chart data as well
as to get the price. So now that that is
done, the only thing remaining from this
custom hook to do is to return the data
in the simplest format possible. So I'll
say return an object that includes the
price, the trades, the OHLCV data as
well as the is connected state which is
set to is WS ready. You can see that now
our TypeScript is not complaining. We're
returning exactly what we need to return
and we're exporting this custom hook
ready to be used wherever and whenever
we need to access real and live data
from the Coin Gecko websocket API. So in
the next lesson, let's use it directly
within the coin details page.
to start working on the details page.
Let's head back over to our application
right now and click on any of the coins
from the coins list. You'll get to a 40
or four, but that's totally fine. What
we need to do is create a new route for
that page. It's going to be within
coins, but then create a new folder
within it for a dynamic ID route. To do
that, you have to wrap it within a set
of square brackets. Then within that ID,
you can create a new page.tsx.
This is going to be the page within
which we'll display our coin details.
So if you save it, you should be able to
see it right here. But how do we figure
out for which coin we're displaying the
details for? Well, thankfully for that,
Nex.js provides us with params. If you
do it like this by turning your page
into an asynchronous page and get params
which are going to be of a type next
page props those params will contain
whatever you put into this ID which is
going to get populated by the URL. So
right now we are on the URL of coins
bitcoin. So we can extract that ID by
saying const dstructure the ID by saying
await perhaps
and then if you say coin details for and
then show the ID you'll be able to see
that we're showing the details for
bitcoin and thankfully that now very
easily allows us to get access to coin
details. So right here we can say const
coin data is equal to await
fetcher that we have created before
that's going to return the coin details
data. And this time we want to make a
call to forward slashcoins slash ID. We
can even provide some additional options
such as the dex_pair_mat
to be a contract address. We'll use this
data soon, but first let's create a JSX
structure to be able to show all of that
data. I'll wrap everything into a main
tag because we're on a new page with an
ID of coin
details page. Within it, I'll render a
section that has a class name of
primary. And then within it we'll render
an H1 that has a class name of text-3XL
and font-bold.
There we can say coin and then we can
render it in bold and render the ID of
that coin like this. So now we can see
coin bitcoin. This will be replaced with
the actual section later on.
Then below we'll display a section of
trend overview. Right below it another
one for recent trades.
And then below it another one for
exchange listings. All of that is going
to be within the primary section. Then
we'll have a second secondary section
with a class name of secondary. And here
we can render all the additional coin
details such as an unordered list with a
class name of
details-g grid. And then within here we
need to properly map over the data to be
able to display it. Now the data we're
getting from the coin data right here is
a bit unstructured like it has all sorts
of different things. So what we want to
do instead is only extract the things
that we actually need.
I'll do that right here by saying const
coin details is equal to an array where
we can define different objects for
different properties. Each one will have
a label. The first one can be like
market cap and it'll also have a value.
So we can now duplicate this a couple of
times for however many key value pairs
we'll have. In this case, let's do like
six. And let's at least provide an empty
string for the value just so it doesn't
break. Now that we have this empty data
for coin details, we could map over it
right here within a grid. So I'll say
coin details.m map where we can extract
or dstructure the label, the value, the
link, and the link text if we got one
from each one of these links as well as
the index. And we can automatically
return a new list item for each one of
these values. But let's make sure that
we have properly indented this and that
all of the values are in the right
place. Here we have the map. Within the
map, we have to have the index as the
second parameter. So that's going to go
right here. And then we automatically
return. Then each one of these allies
will need to have its own unique key
which we can set to index. and a
paragraph that'll render the label. So
right here I'll render a P tag with a
class name of label and it'll simply
render the label. Immediately you'll see
all the labels say market cap because we
haven't yet updated the actual labels.
Then below it we'll check whether it's a
link. So if we have a link in that case
we want to display a div. Else if it's
not a link we'll simply display a p tag
that will render the value. So this P
tag will have a class name set to
text-base
font- medium and we'll render the value
and in the div if it's a link we need to
actually give it a class name of link
and within it we want to display an XJS
link with an href going to whatever the
link is with a target of underscore
blank. So it opens it up in a new screen
within it. We can display the link text
or if that doesn't exist maybe just the
label and we can also write below the
link render the arrow up right which is
coming from lucid icons. Give it a size
of 16 and close it right here to note
that this is actually a clickable link.
Also, I almost forgot. Right here in the
second section, we'll have a currency
converter right here. So, you can
convert a specific cryptocurrency into
all sorts of different worlds
currencies. Then, we'll have another div
which we'll use to wrap everything else
with that'll have a class name of
details. So, make sure to put this H4
and the ul within the details part like
this. And then below that div, but still
within the section, we'll render a P tag
for the top gainers and losers
component. Some of this data coming from
the coin details will be static like I
mean it'll update on reload but won't be
fetched from the websocket. But
everything else that we have here such
as the coin price, the trend overview,
the chart, the recent trades and
exchange listings, the recent trades
will be coming from the websocket. For
now, let's format the coin details data
by providing the value to the market
cap. That's going to be format currency,
which is a utility function we created
where we can get the coin dataarket
data.cap.USD.
USD like this and you can see now the
market cap appears right below. Then the
second one will be the market cap rank
and this will be just a template string
where we use the hash sign to denote the
rank and then we'll render the coin data
market_cap
rank.
Looks like bitcoin is number one in
market cap. Then we'll do total volume
and we can format it properly by saying
format currency and we'll pass in the
coin data dot market data total volume
USD. After that we'll render the
website. Some coins have their official
coin website. We can get that by
providing not a value. Value is just
going to be a dash but rather a link. So
the link to the website will be coind
data dot linksh homepage zero and link
text will simply say homepage. After
that we can have another this one will
be for the explorer. So directly linking
to the blockchain site, we'll copy the
same structure that we have here, but we
will change the link to point to links
blockchain site zero. And the link text
will say explorer.
And finally, we have one more. I'll
duplicate it here. This one will say
community link or just community. It's
going to point to subreddit
URL. And we don't have to put the zero
right here. and it'll simply say
community.
Perfect. So if you save this, you can
now see that we have all sorts of these
different links and data points
that we will structure in a way to look
like this. Coin details, market cap,
rank, total volume, website, community
link, and so on. Perfect. And now that
we've fetched all of this data that
doesn't change super often, we can also
change the data that we're going to
stream in. So right here still within
the same page at the top of the coin
details right below the coin data we can
fetch the coin OHLC data which is the
data for the candlestick chart. I'll
make this equal to await fetcher. It'll
be of a type OLC data and we can call it
by going to forward/coins
id and then forward slashlc.
We can also pass some additional options
such as the versus currency
that'll be set to USD for now.
Then we can also get the number of days
which is going to be set to one. The
interval which by default will be set to
hourly and precision which we can set to
full.
So now again we're doing one call after
another but instead it might make more
sense to wrap that all in a promise that
all. So we'll say const we're going to
have two different values. The first one
is going to be the coin data. The second
one is going to be the OLC data for the
candlestick chart. And these are going
to be equal to a weight promise.all
array. And now we simply have to pass
the two fetcher functions. the first one
and then the second one one after
another and then we can remove the two
independent calls fix this typo right
here in the word interval and I believe
that we should be good perfect so now
we're getting the coin OHLC data as well
now for us to be able to display this
data properly we can create a few more
variables that'll help us extract
exactly what we need such as the
platform of the coin which is going to
be equal to coin data
dot asset_platform
ID and if it exists then we're going to
get the coin data detail platform
question mark dot and then we're going
to dstructure within an array the coin
data
platform id or if it doesn't exist the
platform will simply be set to null.
This is detail platforms. You can see
that typescript nicely noticed that the
platform doesn't exist on the coin data.
Then from the platform we have to figure
out which network is it on. So const
network is going to be equal to platform
question mark. Gecko terminal URL
dotsplit with a forward slash. And we're
going to get the third part of the URL
split. That's the part that contains the
network or null. And finally from the
platform we can also extract the
contract address which is going to be
equal to platform question mark dot
contract address or if it doesn't exist
I'll set it to null. Now that we get all
of that we also need to get something
known as a pool. See in cryptocurrencies
we need to find the best trading pool
for each coin so the live data can
connect to a real market. A pool
essentially is a trading market for a
token on a decentralized exchange. You
can think of it as a pair's liquidity
bucket. For example, token USDC. So BTC
USD, that's one pair. Within that pool,
the trades will happen and the price and
trade data comes from there as well.
Essentially, the pool identifies which
market the app should subscribe to for
real-time updates. So for that reason,
you can head over into the coin
gecko.actions.ts
and alongside the fetcher, I've also
created another function called get
pools that'll make it super simple for
us to get access to the most relevant
pool. I'll provide this get pool snippet
of code within the video kit link down
below. Once you add it here, let me
explain it. It is just a regular
function that we'll use as a utility
function, meaning that it'll help us to
do something. Then it accepts a couple
of props such as the ID, the network and
the contract address. Both of these are
optional. Then we first create a
fallback of a type pool data. So each
pool will have an ID, an address and
name in a network starting as empty.
Then if both a network and a contract
address exist, we can get the pool data
by using the fetcher and a corresponding
URL that'll give us access to the pools.
Once we get it, we're going to return
the first pool. But if we don't have the
network and the contract address, we can
search for a pool by using onchain
search pools endpoint and pass the query
equal to the ID and then we return it
just the same. So now that we have this
function, we have everything we need to
be able to pass these details into it.
So I'll say const pool is equal to await
get pools. Okay, this is the utility
function we created and to it we need to
pass the ID, the network and finally the
contract address. Great. Now that we
have this information, we are ready to
start fetching real time data. So let's
create a new component right here in the
components folder that's going to be
called live data wrapper.tsx
and run ource. Once you have it, you can
head back over here and render it right
here at the top of the primary section.
Live data wrapper. It'll also accept
some children as a prop. So for now, we
can just pass an H4 that's going to say
exchange listings.
And we can remove the rest of the
boilerplate code that we had from before
like this. Now into the live data
wrapper, we can provide a couple of
props such as the coin ID which is going
to be equal to ID, the pool ID set to
pool ID. We'll also get the coin which
is going to contain all the coin data as
well as the coinlc
data which is going to be equal to the
value with the same name. Perfect. And
now we can finally get into the live
data wrapper component and implement it.
It'll make the coin details page feel
alive. It's the piece that'll keep the
price, the chart, and recent trades,
updating them in real time while still
showing the rest of the coin info.
Without it, the page would only show a
snapshot that's already outdated. It
also keeps those live parts organized in
one place, so the page stays clean and
it's easy to extend within it. To
separate those cards or different pieces
of text, we'll use a chat and separator
component, which is just a super simple
blank line basically. So, we can go
ahead and install it right here within
terminal by running mtpx chats and at
latest add separator. And then we can
start rendering the JSX. We can start by
turning this into a section that'll have
an ID of live data wrapper within it.
For now, we can put some placeholders
like a P tag that's going to act as the
coin header.
We can then separate it using the
separator component coming from
components UI with a class name set to
divider.
Then below it, we'll render another div
with a class name set to trend. That'll
display the candlestick chart.
Thankfully, we have already created it
so far and it's not coming from Lucid
React. Uh, it's actually coming from our
own components
and it accepts a coin ID which we
thankfully have because we passed it
through the props. So, we have to
dstructure everything that we pass
through props right here. The coin ID,
the pool ID, the coin and the coin OHLC
data. So, let's get it right here. We
can dstructure them.
Make sure to add commas in between.
And these are going to be of a type live
data props. And I'll put them one next
to another.
We also get access to the children
because right here within the live data
wrapper, we're passing something into
it. Okay. So now we have the coin ID.
What else can we pass into it? We have
to pass the data into the candlestick
chart. So I'll say data is equal to coin
OHLC data just like so. And if you save
it, you'll be able to see a chart that
we have created so far that'll render
the data for this specific coin. In this
case, I think it's Bitcoin. Within this
candlestick chart, we can also render
children. In this case, we'll render an
H4
that'll say trend overview. The reason
why we're repeating this candlestick
chart here is because we'll make it
dynamic in real time on this page.
Finally, we'll separate this using a
separator with a class name of divider.
Then below it, we want to display some
recent trades for that coin that we can
get access to by using our custom hook
conststructure
the trades by making it equal to the use
coin gecko websocket and pass the coin
ID and the pool ID. Remember this is
that custom hook that we created before
and we made it accept these things and
it returns the price and one of those
things is the trade. But since we're
using use effect here in this custom
hook, we have to give it a use client
directive. And if you try to use a
custom hook within a server component,
that's not going to work. You have to
make that component client rendered as
well. So give this one a use client
directive.
That will give us access to the trades.
So now we can render again some columns
for the tables where the trades will be
rendered. We can call it trade columns
of a type data table column specifically
that'll have access to an array of
trades and it'll be equal to and now
once again for like the fourth time in
this course we need to render the header
the cell class name
as well as the cell meaning the data
that we want to show for each one of
these uh columns in the table. just so
you don't have to type it out by hand.
I'll provide you with the final trade
columns within the video kit down below.
So you can just copy them and paste them
here. It'll look something like this. We
get the price, then we get the amount,
the value, the buy and sell, and the
time. And once we have those trade
columns, we can simply reuse our
existing data table. So below the
separator, I'll say if trade columns
exist. In that case, render a div
that'll have a class name equal to
trades. And then render an H4 within it
that'll say recent trades. And finally
below it, render a data table with
columns set to trade columns.
The data set to trades that we're
getting from the web hook. The row key
set to we can get access to the whole
row but also to the index and then set
the key to be equal to the index of the
row. And finally, we can set the table
class name to trades table. And we can
close it right here. It'll look like
this. And it looks like in one of these
elements or in a couple instead of
format price, we're going to say format
currency. I believe this should already
be done for you. I think I renamed it at
one point in time from format price to
format currency.
So this is going to be a very simple fix
and you also have to import the time
ago. These are coming from utils. It
looks like on the day of the recording
of this video, Bitcoin had a very
positive day. Now Bitcoin is not a super
active token. So it looks like there was
one recent trade right here, but it's
hard to be able to see recent trades in
real time. So instead, we can go to all
coins and search for another token. Like
I was doing some research and a couple
of times I came up with a token called
Audierra. So what we can do is go to
localhost 3000 coins Audierra and this
one has a lot of traffic which means a
lot of recent trades. So if you go here
check this out without reloading. You
can see how new trades are just coming
up and changing. So, this is pretty cool
for traders or for people who really
need that instantaneous real-time
feedback streaming in through a
websocket. That makes a huge difference.
That regular case where you pull for the
updates every minute or so. And when you
compare that to streaming the data in,
that makes all the difference. I mean,
you can see while I was saying this, how
many new trades came in. It's
instantaneous, live, and without needing
to reload or update the page. But this
is not it. will also make the coin
header including the price real time
price as well as the chart update in
real time. So let's do that in the next
lesson.
So how do we go from this chart that is
static and reloads once you well reload
the screen to something that updates in
real time either in 1 second or 1 minute
intervals. Well, let me show you. We can
do that right here within the live data
wrapper because as I said everything
will be happening here. So let's add
this additional button that we didn't
have before for choosing the frequency.
I'll create it as a new use state field
called live interval and set live
interval. And that's going to be of a
type either 1 second or 1 minute. So
let's do it like this. 1 second or 1
minute starting with 1 second. And why
are we doing this within the live data
wrapper and not for example within the
homepage where we already created the
chart or within the chart component?
Well, that's because I specifically want
the chart on the details page to be live
whereas the one on the homepage is going
to be not live. Why? Well, if you later
on add user accounts to your application
and maybe a subscription package, that
would mean that you can charge extra or
you can allow users to join your
subscription to be able to get real data
because this is definitely a feature
that you can charge money for. If of
course you want to make both of them
live, you can totally do that. So now
that we have this live interval, I'll
pass it over into the use Coin Gecko web
socket and it's going to give us back
the OLCV data. We can then use that data
and pass it into the chart
by passing a couple more properties. So
what I'll do is say live OHLCV is going
to be set to OLCV.
Mode will be set to live. Initial period
will be set to daily. A live interval is
it minutes or seconds is going to be set
to live interval. and set live interval
is going to be the setter function that
we just created above. So now you can go
to the candlestick chart. We can accept
all of these new props such as the live
OHLCV initially set to null, the mode,
which by default will be set to
historical, but now we're passing the
live mode, the live interval, as well as
the set live interval. We're passing
these as props. Now to be able to update
it in real time we'll have to create one
additional ref. So I'll do it right
here. const prev oh hlc data length.
Okay. So we will get access to the
length by creating a new ref of a type
number which is going to start as data
question mark.length or zero. That'll
allow us to track it and then update it
so we can move the chart to show the new
candle. Then within fetch data right
here, we'll wrap this into a start
transition. Start transition means that
it's non-blocking for the UI. So it's
going to accept a callback function. And
within it, we will set the data like
this. Then now since we have access to
this set live interval, that means that
we need to create a new function that
allow us to change the frequency what
I'm doing here. So we can do that right
below the fetch OHLC data handle period
change. Oh, but looks like we already
created that one. We have it here. So if
we choose the same period, it's fine.
But else I'll remove the transition from
here since we added it above. And we
will set the period to a new period as
well as fetch the new data by passing in
the new period. And now within this use
effect, we'll have to do some math. So
here we're creating our first candle and
adding it to a series. Here we'll have
to create a new variable called merged
of a type OHLC data array. And then if
we're tracking the live data in that
case we want to get access to the live
time stamp which is going to be equal to
live
zero like this. Then we need to check if
we need to update an existing candle if
we're within the same time period. See
how right now I believe this one
updated. Yeah. Check how it's moving
from red to green because we're still
within that specific minute. But if a
new minute passes, then we need to
figure out whether we need to create a
new candle. So let's figure that out.
const historical
candle is equal to converted 2 seconds.
Then we need to get the last second. So
convert it to seconds.length
minus one. And then we need to check if
last historical candle and last
historical candle zero. So the first
value of it is the same as the current
time stamp. So live time stamp. In that
case, we want to update the last candle
with the right data. So I'll say merged
is equal to and then I'll create a new
array spread the converted to seconds
data. So converted to seconds dot slice.
So we're going to get the first piece of
it from 0 to minus one. And then we'll
pass the new live OHLCV data. Else if
that is not the case merged will be
simply a new candle. So we can say
merged is equal to. We'll spread the
converted to seconds data and we'll
append a new live candle. Finally, we're
going to exit this else if and create an
else right here. If we're not live,
merged will be equal to the converted to
seconds data because that's how we've
been tracking the data when we weren't
live. Finally, we want to sort the
candles ascending in time by saying
merged. sort A and B and we take the A0
which is the time stamp minus B 0 to
show that we want to render it in an
ascending order and then we can say con
converted is equal to convert OHLC data
and pass in the merged data. We want to
update the candle series ref. But I
think we're already doing that here. Oh
wait, it looks like I did it in the
wrong use effect. The live updates
should have been happening in the second
use effect that actually takes a look at
the OHLC data. So we need to grab the
part that we coded so far. This includes
everything up to the converted part.
Everything with an if right here. So
copy that, remove it from here and
instead move it right here within the
second use effect. It is this one right
here
below the converted to seconds.
And then you'll notice that here we
don't have to recreate the converted
because we already have it. But now we
have to pass the merge data right into
it instead of the regular candlestick
data. Then we do the candle series refet
data converted. And we also have to fit
content when the data changes
specifically the period not on live
updates. So I'll check that right here.
const
data changed is equal to prevlc
data length.curren
and if that is not equal to oolc
data.length length that means that it
has changed and if that is the case so
if data changed or if mode is set to
historical in that case we can fit the
content for the time scale what we have
been doing here
let's also close it properly right here
and we also have to update it with the
new data so prevcurren
is data.length length and here I'm going
to add now the data the period the live
data as well as the mode and the only
thing we have to do right now is head
down to where we have the label and then
below we can check whether we have the
live interval and if that is the case
then we can render a new div
but let's make sure to close it properly
that has a class name set to button
group within
a span that says update frequency and
we're trying to match something looks
like this but this is on the finish
site. So head over to the current one
and to a specific coin like let's go to
Cardano. And here you can now see update
frequency. But let's style it a bit by
giving it a class name set to text-sm
margin x of 2 font-medium
and text-purp.
Then we can display the buttons. And
that's going to be very similar to these
period buttons. So, I'm going to copy
that and just paste it below the span.
And these are going to be called live
interval buttons. We get the key. We
check for the live interval to see which
one is currently enabled. And if it is,
we give it the button active or not. And
for the on click, we want to check
whether a set interval function exists.
And if it does, we then call set
interval and pass in the proper value.
Now, if you save this, you'll be able to
see the update frequency button appear.
And now, if you click 1 minute, looks
like I got an error saying invalid or
unexpected token. Let's see where this
set interval is coming from because it
should be coming right here from props.
Oh, it's set a live interval, not just
set interval. So, if we fix this and
reload and press one minute. Okay, looks
like it changes, but this coin doesn't
make many changes anyway. So, we can
head back over to the coin that I was
on. That is localhost 3000/coins
slawier.
And here you can see on second that it's
really getting updated. But when you
switch it over to minute, it's not going
to update for a minute and it's going to
stay as it is. So now we can also update
the frequency and check out the recent
trades. It's always super cool to see
that happening live. Pretty cool. Right
now we haven't checked out the whole of
the coin details page for quite some
time now. And this is how it looks. It
looks like we are missing the coin
header. The chart is taking the full
screen and then we only have the recent
trades and all of the other data which
we have fetched but it doesn't really
look that good. So in the next lesson
we'll focus on making this data look
good and we'll also add a whole other
aside to this main content. This is what
we want to achieve. Some info about the
token changing in real time. Then the
converter where we can see what is the
value of the token in different
currencies. We stay with the trend
overview for the live chart. Then the
coin details and maybe we even add the
top gainers and losers. We'll see.
There's also the exchange listing. So,
there's so much more stuff that we can
add to this. Oh, and check this out.
Looks like this icon is a GIF, so it
actually moves. Some amazing stuff. Very
dynamic, and it changes in real time.
So, let's finish the coin details page
in the next lesson.
In this lesson, we'll display the rest
of the information like the coin header
info, the converter, and we'll also
style how we're rendering the coin
details. Then I might also give you a
bit of a challenge to render yet another
table only if you want to display the
exchange listings and also maybe the top
gainers and losers if you want to do
that as well. But with that in mind,
let's dive right in. Back within the
code, I'll create a new component within
the components folder and call it coin
header.tsx
and run rafce. As soon as you create it,
we can import it directly within the
live wrapper,
we'll do it right here at the top. So,
instead of this placeholder that we had
before, that's going to be the coin
header.
Perfect. Now, into this live data
wrapper, we also pass this coin data,
but we never used it so far. So, now we
want to pass it into the coin header to
be able to extract the coin information.
So, I want to say name is equal to
coin.name.
Then we can get the image which is going
to be coin that image.large.
We can also extract the live price which
is going to be price question. USD or if
that doesn't exist it's going to be
coin.arket
data.currencore
priced.
Oh, and the price is coming directly
from the use coin gecko websocket. It's
one of the three things that we are
listening for changes on. So that's
going to be coming right here
from the custom hook that we created.
Then after that, we can also show the
live price change percentage
in the last 24 hours. And that's going
to be equal to price question mark.24.
Or if we don't have the live price, it's
going to be coin.
data.pric_change price_change
percentage 24 in currency USD and here
we're missing 24h as in hours. Then we
want to get the price change percentage
in 30 days like this.
That's going to be equal to coin
dotarket data dot price change
percentage 30 in USD like this. And
finally the price change in 24 hours
which is going to be equal to coin
dotarket data dot price_ange_24hour
in currency USD. So now we're passing
all the necessary coin information to
the coin header. So let's simply head
over into the coin header and let's get
access to all of these prompts that
we're passing in. That's going to be the
live price change percentage
24hour. It's going to be the price
change percentage
30 days, the name, the image, the live
price, and the price change in 24 hours.
And all of that is of a type live coin
header props. And now that we've got
those props, we can also create some new
variables that are going to help us
later on, such as is trending up.
And we can know that if the live price
change percentage in 24 hours is greater
than zero. In that case, we'll be able
to make it green. Same thing for is 30
day up like this. 30day up and price
change percentage 30 days is greater
than zero. And in general is price
change up. That's going to be price
change 24 hours greater than zero. Of
course, that's also a new constant that
we're creating. And to be able to
display the data, we can nicely format
into the array called stats. Each one of
these stats will have a label of a type
string.
Then it'll have a value like live price
change percentage 24. Then it'll have
the is up. So is it trending up in this
case the formatter that we're going to
pass that's going to be the format
percentage function as well as the show
icon. It can either be true or false. So
the first one is actually displaying the
today value the price change today.
And looks like we have to import this
format percentage from the utils. That's
good. And now we can duplicate this two
more times.
The second one is going to be for 30
days. So 30 days. Price change
percentage in 30 days is 30 day up and
true. And finally for the last one we'll
show the price change in 24 hours like
this. So we can use the price change 24
is price change up as well as the format
price in this case because it's not a
percentage. So format currency as well
as show icon set to false. and make sure
to import format currency coming from
our utils.
So now that we have the stats and we
have these variables that we're passing
as props, we are ready to render it all
within JSX. So first things first, I'll
give this div an ID of coin dash header.
And directly within it, we'll render an
H3 that'll just render the coin name. So
you can see Audi right here at the top.
Right below it, we can render another
div that'll have a class name set to
info. And within it, we can render a new
image
with a source of image of the coin. An
all tag is going to render the name of
the coin with a width of about 77 and a
height of 77 as well. And you can see
the token appear right here. Then within
it, we'll render a div that'll have a
class name of price- row. And within
here, we can render an H1. We'll call
the format price or the format currency
function and pass in the live price
right into it. And if you pay close
attention, you might be able to see this
number shift from 180 to maybe like 181
or 179. Oh, there we go. It just
switched over to 181 without us having
to reload the page. And here we can also
render a badge. That'll be like a SHAT
CN badge. So we can just install it.
I'll do that right here by running MPX
SHAT CN latest addge.
And we can then import a badge right
here at the top. And then just use it
like so.
I'll do it right here below this H1.
will render a badge
with. We don't need a variant in this
case, but rather just a class name
that'll be set to CN for class names
badge. And then we can also depending on
whether it's trending up or down, either
give it a badge- up or a badge dash down
class name.
Within the badge, we can render the
format percentage function and into it
pass the live price change in the last
24 hours
and that's going to look something like
this minus 24%. Seems like this one is
down a lot. We can also if it's trending
up render the trending up icon. So
trending up
else we will render obviously the
trending down icon and we can just say
over 24 hours like this. So this one
went down over the 24hour period. We can
see that very clearly right here. And
now we can go below these two divs and
render a ul an unordered list to show
the rest of the stats. So I'll give it a
class name of stats and within it we'll
map over the stats by saying stats.m map
where we get each individual stat and
for each one we just return a list item.
This list item will have a key equals to
stat label
and within it we can return a p that has
a class name of label
and it'll simply render the stat label.
So if you save this now, you'll be able
to see exactly what I mean. Today 30
days price change over the 24 hours.
These are the stats that we prepared
right here. So now let's also render
their values.
To do that, we can head over below this
speed tag and form the div within which
we'll keep track of the values.
And let's give it a class name
set to CN
value. But then also we're going to give
it a text green of 500 if stat is up.
Else we're going to give it a text dash
red 500 if stat is not up. So I'm simply
going to add a not at the top. And then
let's display the stat directly within
this div. I'll render a P tag that's
going to say stat.form
and pass in the stat value. Okay, so 24%
117% and the price change is only minus
0.6 in the last 24, which is actually a
lot since the coin value is 1.8. But
take a look at this. In the last 30
days, it has actually grown by almost
double. So if you thought that this is
not good, well if you take a look at the
larger period of time actually it's
performing incredibly well. And finally
we can show the icon. So stat do show
icon. If that is true then we have
another turnary operator. Stat is up.
And if it is up then we render the
trending up arrow like this.
else. We're going to render the trending
down icon like so. And we can style them
a bit by giving them a width of 16
as well as the height of 16. And you can
duplicate those and also add them to the
second icon. So, it's going to look
something like this. Perfect. This is
looking so much better. If I make this
full screen, it's going to look like
this. A bit weird because we're missing
something. You can see that we're
missing all this outside extra space.
And this is a great little design lesson
showing you just how important empty
space is. It seems like all of this,
including the trend overview, the chart,
and the coin overview, it needs to be
within an additional container to
provide that extra space. So, let's
implement that right away by heading
over into our app coins ID page right
here. First, I have this main with a
coin details age. Hopefully, you notice
this mistake and you have page right
here because when you apply the page,
that'll actually apply all the
additional padding on the side. And now
you'll be able to see that we have the
nice coin overview. And also on the
right side, the coin details now show up
nicely, which for you might have
happened a long time ago if you notice
that missing P. Next, we'll implement
this converter from which you'll get to
know which coin has what amount of value
in other currencies. For that, we'll use
a SHAT CN select. So, if you head over
to it, we can just very easily install
it by running MPX shad CN at latest add
select. And once you add the select,
you'll also need to add the input. So,
search for or just command K and search
for input. And then you can install the
input as well by running MPX shaden add
latest add input.
Once you install them, you'll be able to
create a new component called converter.
So right here in the components, create
a new converter.tsx
component and runce.
You can very easily just import it over
to your details page here where we have
the placeholder for the converter and
just self-close it right here. Once you
do that, if you scroll down, you should
be able to see a text that says
converter right here. To it, we'll have
to pass a couple of things such as the
symbol, which is going to come from the
coin data symbol. the icon coming from
coin data image small as well as the
price list which is equal to coin data
dot market data dot current price. So if
you pass all of these three things
you're ready to head over into the
converter component accept these three
props and implement it. So let's just
dstructure them. the symbol, the icon,
and the price list. All of which are of
a type converter props. And we can
declare a couple of different states.
They're going to manage the values.
First, we need a new use state for the
currency that we're currently viewing
the exchange rate for. And we can start
it with USD. Then another one use state
that's going to be the amount that we're
displaying at the start set to a string
of 10 for example. And then we need to
convert it. So I'll say con converted
price is equal to first we'll turn it
into a float by saying parse float and
pass in the amount or just zero like
this and multiply it by price list and
then the specific currency that we're
trying to exchange to or zero once again
like this. So that should look something
like this. In parenthesis, parse float
amount or zero times price list or zero.
Those zeros might not be necessary, but
just to stay on the safe side. And since
we're using the use state, we'll have to
turn this into a client rendered
component. And now we can render the JSX
of this converter. So starting with a
div that has an ID of converter. Then
within it we can render an H4
that'll say converter
and we can also display a symbol.
So I'll say symbol dot to uppercase like
this. So we know that this is a beat
converter for example. Beat is the
symbol for the auda for example similar
to what BTC is for Bitcoin. So, if we
head over here, Bitcoin, looks like
Bitcoin is up 1% in the last 24 hours.
And it's so cool to see the price change
live. Um, but yeah, if you go here, it
says BTC converter.
Then we can create an input wrapper. So,
that's going to be a div with a class
name input-w.
And within it, we'll render the input
with a type of number, a placeholder of
amount,
a value of amount,
an unchange
where we get the event and we can set
that amount and a class name that'll
simply be input.
So if you save this, you'll have to
import the input as a new component that
we installed from shad CN. So just say
input
and it should autoimp import it not from
post CSS. That's a mistake. It should
autoimp import it from components UI
input.
So now you should be able to see 10.
That's the default value that we want to
exchange. For example, 10 bitcoins into
USD. That will be a lot. And we can also
below the input render a div that'll
have a class name set to coin info. and
within it an image
source of icon alt of symbol
width of 20 and height of 20 as well and
below it we can render a p tag symbol
dot to uppercase so here we're basically
saying 10 btc
we can also wrap all of that together
into something known as a panel so I'll
create another div that has a class name
of panel and then we can wrap this input
wrapper that we have here into the
panel. So now it's going to appear like
a part of something bigger. So we can
display another one of these inputs
below to show the final result.
So now if you go below the coin info and
below the input wrapper here we can
create a div with a class name of
divider
and then within this div we can create a
self-closing div with a class name of
line and below it we can render an image
with a source of forward slash
assets/converter.svg
SVG with an al tag of converter, a width
of 32, and a height of 32 as well with a
class name of icon. This is one of those
images that we pulled from the start. If
you remember, it's going to be within
our public and then here, but we just
didn't put it within the assets. It's
directly within the public folder. So
now the icon appears. Now we can go
below this divider div and we can create
the output wrapper. So a class name of
output dashwrapper
and here we can render a p tag within
which we can format the currency.
Pass in the converted price to currency
and false and then you'll be able to see
the actual number of dollars that 10
bitcoin is right now. It's almost a
million dollars. Like if you do 12, you
cross that. So maybe you're going to be
converting much smaller amount like 0.1
or 0.01 or something like that. And now
we need to make it so that people can
see that bitcoin in their own native
currency. For that we'll have to use the
select coming from shaden. So head over
to shad and select and what you can do
is copy this select usage and then paste
it right below the P tag. First we have
the select and on it you can provide a
value of currency and on value change
you can provide set currency like this.
You'll also have to make sure to import
all of these things. So I will just copy
the imports and add them at the top of
this file. right here. And then that's
going to look something like this. But
we don't want to have apples and
bananas. What we want to have instead
are real world currencies. So to do that
to our select trigger, we can give a
class name of select-asht trigger and a
value of currency.
for the select value as the placeholder.
I'll simply say select and give it a
class name of select dash value
and we're going to make it not
self-closing and within it pass a
currency
to uppercase. So now it says USD you can
see that right here in the placeholder.
And finally for the select content we
can give it a class name of select
dashcontent
and data-con converter prop not a class
name and then we don't need this select
group rather directly within here we'll
get access to object keys
of the price list. Then we're going to
map over it. Get each unique currency
code and for each one of them return a
select item. We'll give it a value of
currency code, a key of currency code
because they're all unique hopefully and
a class name of select dash item. Then
within the select item, you can simply
pass the currency code dot to uppercase
to display them all right here. Now
let's spell this currency code properly
because we're mapping over it right here
and here. If you do that and scroll
down, you'll now be able to see the
converter. And if we want to know how
much is for example one bitcoin
in let's do something like
audustralian dollars. You can see that
it's 130,000. This means that our
converter is working. We can also try in
something like rupees and it's going to
be almost 8 million. Now, if you open
this up in full screen view, you'll be
able to see the beauty of this dynamic
realtime coin details page that we just
developed. You can change the period.
You can update the frequency. You can
move around the chart. You can take a
look at the coin price in other
currencies. You can see the coin details
and even the recent trades that impact
the price. This is amazing. In the final
version of the application, I even took
it a step further and implemented the
exchange listings so you can see where
it is actually being traded as well as
the top gainers and top losers tables.
I'm going to leave those two totally up
to you. The top gainers is right here in
the coin details page at the bottom. So,
if you don't want to do it, you can
simply remove this placeholder and it'll
be gone. But if you do, you can try it
on your own or just see how I
implemented it within the final code of
the application. It's available
completely for free within the video kit
down below.
But with that in mind, let's go ahead
and commit all the changes that we
implemented on this amazing coin details
page.
I'll open up the code, specifically the
terminal. Run git. Run git status to
check on which branch we're on. It looks
like we are on the coin details feature
which is great. You can run git add dot
get commit-m
implement coin details realtime
page and run git push. We'll of course
have to set the upstream to the origin
and as soon as it's out back on GitHub
you'll be able to compare and open up
the new pull request. So let's do just
that. And since this was a huge PR,
let's see if some bugs sneaked in. Dr.
Rabbit or how I learned to stop worrying
and love the code review. Well, let's
hope we'll love it after we get back the
results. So, I'll pause recording right
now and I'll be back in a couple of
minutes once we have the complete review
so we can analyze it so we can make it
more scalable and less errorprone. In
this PR, we added almost 2,000
additional lines of code that result in
a new dedicated coin details page
displaying market information,
introducing live candlestick charts with
real-time price updates and currency
converter with recent trade section
showing live market activity. So, let's
see whether we have some bugs within the
candlestick chart. There's a potential
race condition with concurrent period
changes. So if the user rapidly clicks
different period buttons, multiple fetch
calls can be inflight simultaneously.
Since there is no request cancellation,
an older request completing after a
newer one could override the data with
the stale results. Okay, this is
actually good. So it suggested using the
abort controller. It's a new ref that'll
abort any in-flight requests. This is
very interesting and I would say quite
an advanced suggestion because it's also
using signals. Alongside that one, we
also have this little minor issue in the
coin header just saying that the icon is
rendered incorrectly when the show icon
is false. Why is that? Well, that's
because the turnary condition only
checks stat.ow icon and stat is up for
the trudity branch. But the else branch
unconditionally renders trending down.
This means that when show icon is false,
like for the price change, the down icon
still appears. Okay. And what we should
have done instead is wrap the additional
turnary within an additional pair of
parenthesis.
That's a good one. So, let me
immediately find this part in the code.
stat.show
icon. Okay, I'll search for it right
here. Show icon.
There we go. And I will simply replace
this part with the new one and remove
the plus signs. And now you can see this
is a nested turnary. So this one acts on
its own and this one is at the start.
Then we have the live data wrapper.
Incorrect fallback for the 24-hour
price. When price change is null, the
fallback uses this instead of the
24-hour equivalent. This will display
the 30-day change when the label says
today.
So this is on line 53 of the live data
wrapper. Oh, this is a mistake here. I
think we should have used the 24-hour
percentage. So let's say price change
percentage 24. Okay, this was another
great catch because here we're using the
30, but since the variable name was
super long, like I wasn't able to see
which one I was clicking. So this is
definitely super useful.
Nice catch. And this isn't even a bug.
So I wouldn't fetch this in code, but
maybe a user browsing the platform would
see an inconsistency and report it to
me, but thankfully Code Rabbit caught it
immediately.
Then we have one within the use Coin
Gecko websocket.
This is a major one. It's missing the
onair handler and the reconnection
logic. So connection failures are
silent. Okay, so the proposed fix is
simply to add the on error so we know
when something happened. So, we can head
back, go to the use coin gecko
websocket,
and right here after on close,
that's going to be I think right here on
line 80, we can also check for the error
and just add it right here.
Perfect. Finally, there is one more
here. Potential runtime error if pool ID
is undefined. So, we are using the
replace without a null check. So what we
need to do instead is simply use the
question mark question mark operator to
set it to a string if it doesn't exist
so we don't get an error. So let's
search for pool ID and we have to add it
right here. Perfect. And the last one is
a major potential issue. There's
inconsistent error handling between code
paths. So the first branch when network
and contract address are provided lacks
error handling and will throw on fetch
failure while the second branch catches
errors and returns a fallback. This
inconsistency could cause the page to
crash when pool data is unavailable for
a specific contract. This is entirely
true. So what it recommends is also
wrapping this one in a try and catch
block. So, if we head over to the coin
gecko actions
and then find that, it's going to be
right here. See how we're wrapping this
one in a try and catch and then a
fallback. We can also do the same with
this one. So, try here, put all the data
there, and then finally do a catch with
an error where we can simply return the
fallback here as well. or you could also
console lockog the error or throw it do
anything but this is going to be more
consistent than not having it at all and
with that we've solved some pretty hard
to find bugs and inconsistencies so I'll
go back and simply push the changes by
running git add dot get commit-m
implement code rabbit suggested fixes
and run get push
as soon as re-updates, we can merge the
pull request right into main. Perfect.
Great work. Now, back within the code,
I'll switch over to the main branch and
run gitpool to see just how far we've
come. What you're seeing right here is
on the main branch. It looks great. And
then don't forget the homepage that we
developed not that long ago. The only
thing that is remaining is the search
model. So let's talk about that in the
next lesson.
Up until now, I walked you through every
single line of code from fetching data,
building these reusable fetchers,
charts, and handling pagenation. And
while following along is a great way to
start, it can trap you in tutorial hell.
The state where you feel you understand
everything you're watching, but your
mind goes blank the moment you open up a
blank file on your own. That's why this
lesson is different. Instead of giving
you the answers immediately, I'm going
to give you the requirements and the
tools. This simulates a realworld job
scenario. A senior developer or a
product manager won't give you the code.
They'll say something along the lines of
we need a global search feature that
opens up when a user presses command or
control K. Here are the APIs go.
Sometimes they'll even give you a design
that looks like this. And very rarely
they'll give you a final working product
that you can check out and then
replicate like what I'm doing right
here. That's exactly what you'll do
today. Develop a global search feature
that opens up when you press command K
and closes and you'll allow your users
to search for any cryptocurrency on the
platform. I've created an active lesson
that contains all the information you
need. The link is in the video kit down
in the description. Go open it, study it
in details, and try to implement this
feature on your own. And if you feel
comfortable with that, feel free to help
yourself with AI. When you know what
you're doing, AI can definitely speed
you up. So once again, the link to a
special active lesson my team and I
prepared for you to implement this
feature is down in the description. So,
pause this video and go and implement it
because in a couple of seconds we'll
continue with the deployment. We'll do
that in three, two, one. Okay, let's go
ahead and deploy this project. I hope
you did well. First things first, make
sure that you pushed all the latest
changes. Make sure there aren't any
issues. Like for me, when I go to all
coins page and then click a coin, looks
like I get a console error. I think this
is happening on the web socket. So, if I
go here and remove this line right here,
it should be good. So, I'll just retest
it. And there it is. You can push that
change if you want to. Then head over to
Verscell and log in. At the top, click
add new. Choose project. And you should
be able to see coin pools somewhere at
the top.
Import it. And then here you'll see your
environment variables.
Back within the code, head over into
your envy
all of these environment variables and
just paste them right here.
Then click deploy. This will take about
a minute or two and you should be able
to see your crypto application live on
the internet where you can share it with
your friends and potential employers as
well as maybe some clients that are
interested in cryptocurrencies. Oh, and
it looks like we have a small TypeScript
warning right here that breaks the
build. We can fix that very easily by
heading over into our next config. And
here you can say TypeScript and then say
ignore build errors and set that to
true. And now we can just do another
commit by running get add dot get
commit-m
small fix and then push. This time on
VEL you won't have to stay here since
the new project was already created. You
can get back to your projects. Reload
and you should be able to see coin pools
where under deployments a new deployment
should be going up right now. Let's see
if we get past 24 seconds. And there we
go. The build is ready. So back into the
overview, click visit to check it out.
And take a look at that. We have a final
version of our coin pulse cryptocurrency
screener application. We have a price, a
beautiful chart, trending coins and top
categories. Most importantly, on the
coin details page, we have this live
price update that just happened. Looks
like Bitcoin is a bit volatile right now
and looks like it's growing. And you can
also modify the frequency of the
updates, modify the chart. It's all
updating in real time and it's so
amazing to see. And on your end, you'll
also have this amazing commander control
case search where you can search for any
kind of a cryptocurrency and see real
updates for it.
This was an amazing build. Huge thanks
to Coin Gecko, not only for sponsoring
this video, but for developing such an
amazing websocket API that allows us to
stream in all of this new data without
needing to pull it every minute or so.
And if you came to the end of this
course and you liked it, you'll love
what we do on JS Mastery Pro.
Specifically for you, I would recommend
checking out the ultimate NexJS course
if you haven't already because we dive
so much deeper into all of the under the
hood Nex.js functionalities. So go ahead
and check it out. Or maybe you want to
develop some animations with Gap or make
your applications more secure by
implementing more comprehensive testing
coverages. If you want to do that or
just advance your developer career, you
can do it all on jsmastery.com. But with
that in mind, thank you so much for
coming to the end of this course.
Amazing job on building this application
and I'll see you in the next one. Have a
wonderful day.
In this video, you'll build CryptoPulse, a real-time crypto analytics terminal with global market stats, trending tokens, a searchable tokens table, and a full token detail page featuring charts, live prices, and exchange data. Powered by the CoinGecko API REST endpoints and crypto WebSocket API to stream real-time, sub-second, ultra-low latency market data β giving everyone the ability to build industry-leading, data-driven crypto and Web3 projects. β CoinGecko API: https://jsm.dev/crypto-gecko π° Use code JSMASTERY10 for 10% OFF your first CoinGecko API plan subscription What you'll learn: - How to use CoinGecko's WebSocket API for real-time, sub-second price streaming and live candlestick chart updates - Building a real-time crypto analytics dashboard with live price feeds and trade data - Fetching cryptocurrency market data using CoinGecko REST API endpoints (coins, markets, OHLC, trending) - Building a crypto price converter with multi-currency support using CoinGecko's market data - Displaying real-time on-chain trades streamed via WebSocket API - Fetch and display trending coins, top gainers and losers, and coin categories using CoinGecko API's discovery endpoints CodeRabbit: https://jsm.dev/crypto-rabbit Junie AI: https://jsm.dev/crypto-junie WebStorm: https://jsm.dev/crypto-webstorm Explore my Pro Content: β Join JS Mastery Pro: https://jsm.dev/crypto-jsm π Become Top 1% Next.js Developer: https://jsm.dev/crypto-nextjs π¨βπ¬ Master Next.js Testing: https://jsm.dev/crypto-testing π GSAP Animations Course: https://jsm.dev/crypto-gsap π Three.js 3D Course: https://jsm.dev/crypto-threejs π JavaScript Course: https://jsm.dev/crypto-cpjsm π Launch Your SaaS: https://jsm.dev/crypto-saas π FREE Video Kit (Code, Figma, Assets, and more): https://jsm.dev/crypto-kit π¨Β FREE TailwindCSS Course: https://www.youtube.com/watch?v=6biMWgD6_JY More courses launching soon! Join the waitlists to get notified! π₯ π React Native Course Waitlist: https://jsm.dev/crypto-native π Backend Course Waitlist: https://jsm.dev/crypto-backend π React Course Waitlist: https://jsm.dev/crypto-react π Tailwind Course Waitlist: https://jsm.dev/crypto-tailwind π AI Development Course Waitlist: https://jsm.dev/crypto-aidev β Β Links not working? β If you're in Nigeria, you'll have to use a VPN due to regional restrictions. Rate us on TrustPilot: https://jsm.dev/trustpilot β Β If something mentioned in the video isnβt listed here or a link is broken: β contact support@jsmastery.pro https://discord.com/invite/n6EdbFJ https://twitter.com/jsmasterypro https://instagram.com/javascriptmastery https://linkedin.com/company/javascriptmastery Business Inquiries: contact@jsmastery.pro Timestamps: 00:00:00 β Introduction 00:03:25 β Project Setup 00:10:54 β Navigation 00:22:09 β CoinGecko API 00:24:38 β Homepage 00:46:00 β Suspense & Streaming 01:16:31 β Candlestick Chart 01:48:54 β Top Categories 02:03:14 β Coins Page 02:21:40 β Custom Hook 02:45:18 β Coin Details Page (Part 1) 03:08:43 β Realtime Chart 03:20:25 β Coin Details Page (Part 2) 03:50:28 β Create Global Search 03:52:24 β Deployment