So what I want to build now is the little header component here which will have the follow button, the name of the current streamer and also all active participants that are currently watching the stream. So for that I want to go back inside of my stream player component. So let's go inside of stream player and let's find index.vsx. And in here inside of the LiveKit room because we're going to need to use context from LiveKit room in order to get the proper number of participants, the connected status and all of those hooks that we use you know throughout our video and our chat component and all of those things. So below the video component add a header component and right now it doesn't exist but I already want to prepare some props so passing the host name which is going to be user.username go ahead and pass in the host identity which is going to be user.id viewer identity which is going to be identity which we have stored from this hook useViewerToken, right?
This is where we have that. Let's also pass in the image URL of the user. So that's going to be user.imageURL. And let's do isFollowing to be the isFollowing prop and name is going to be stream.name like that. Great!
So now that we have this let's go ahead inside of the stream player and let's create header.tsx like this. Let's mark this as use client and let's export const header and return a div header component. And now go back inside of the string player index and you can import header from .slash header as we did with our chat video and chat toggle and let me just assign this here like this. So now let's go ahead and fix this little TypeScript error that we have because of this props here. So inside of our header component, we're going to create an interface header props like that and let's go ahead and assign everything we need.
So we need the image URL, which is a string, host name, which is a string, host identity, we need the viewer, identity, so all of these things are strings and now we have is following which is a boolean and we need the name of the stream which is a string and now we can go ahead and assign those props so header props here and we can go ahead and extract the image url, host name, host identity, viewer identity and is following like this great And now what I want to do is go ahead and start to render this component. So let's give this div a class name of flex-flex-col-1 lg-flex-row gap-y-4 lg-gap-y-0 So we are doing some responsive stuff here, right? On large devices, all of this is going to be in a row next to each other. But on mobile devices, it's going to use flex call, right? And this should just be flex call not flex call one my apologies like that and this should have a dash next to it like this and then let's do items start justified between and px4 like that now inside I'm going to go ahead and write a class name here flex items center and gap x3 and now let's reuse our user avatar component which we have from dot dot slash user avatar but I'm gonna change it to gum from slash components like this and now we can go ahead and pass everything we need from this so we're gonna give it the image URL which is image URL we're gonna pass in the username which is the host name.
We're gonna need the size and this time it's gonna be large and you can already see it appearing right here and now we're gonna have to pass isLive prop and let's see if we have the isLive prop. It looks like we don't have isLive. Well, we're gonna create the isLive prop from our hooks from LiveKit, because remember we are doing the header component inside of the LiveKit room component, meaning that we have access to all the participants, connection state, our stream, everything. So for now I'm going to hard code this to true so you can see how this looks now. And let's also add the show badge property because since this is a large type of avatar it's gonna look better.
You can see it right here how it looks. Perfect. So we have this streams user and the badge, right? And we hard-coded isLive the truth. So we're gonna leave it like this for now.
And then below that I want to open up a div with a class name space y1 and then another div with a class name flex items center and gap x2 so it's going to hold multiple items inside and the first one is going to be an h2 element which is simply going to render the host name and a class name is going to have text large and font semi-bold like this and then below that we're going to add the verified mark so I want to create a little component for that so we can reuse it wherever we need. So inside of your components folder create a new file verified-verifiedmark.tsx like this. And let's go ahead and import check from Lucid React and let's export const verified mark and let's return a div which renders that check icon and I already want to use that so go inside of header and what I want you to do is below this h2 element render the verified mark component from verified mark and I'm going to change the import to go to slash components like this so now you should just be seeing a plain check icon next to it so you can see in real time how we're going to modify these classes.
So let's give this wrapping div a class name of padding 0.5 like this, flex item center, justify center. Then now let's give it a height of 4 and a width of 4 rounded full, so it's going to be a circle and let's give it bg blue 600 like this, there we go and now let's go ahead and give this check a class name of h 10 pixels specifically, width of 10 pixels specifically as well, text primary and let's give it a stroke of 4 pixels like that And there we go. I believe now we have a check mark which is similar to what other streaming services have and it uses our primary blue color. Let's go back inside of our header component now. And now I think it's time for us to add some hooks here so we can actually render all the dynamic content we need, like the amount of viewers, right?
So first, let's go ahead and get all the participants. So const participants. So we're gonna use the same hook, which we just used inside of our community, inside of our chat community, right? So in here to render the participants and to filter them, we use the hook called use participants, right? So we're gonna reuse that.
So that's how useful these hooks are because they all live in the same context. So they all have the exact same information and they are all connected in real time so make sure you import useParticipants from LiveKit Components React and we're going to use that as a count for our participants and now we have to get the actual streamer so we're going to call him participant street a single right and now let's do use remote participant from live kit components react and make sure it uses the single one so make sure you add the use participants and the use participant use remote participant hook And now we have to pass in the identity here. So let's pass in the host identity like this. And now let's go ahead and do const is live to be, So turn this state of the remote participant into a Boolean by adding double exclamation points. So just add participant and now you can see that this is live hook is a type of Boolean, right?
We could have used this as well but then you can see that it's this type. So I want to go ahead and make sure that this is a Boolean. And now let's go ahead and add const participant count to be participants, right? So we're using the result from this hook, participants.length. And let's also do minus one, because we know that one of the participants is going to be us locally streaming right that's why we had that duplication inside of our community if you remember which we had to dedupe and now let's go ahead and write const host as viewer to be openVectics host dash and render the host identity inside and then what we can do is check if we are the host so const is host is viewer identity equals host as Viewer like this great so now we have all these necessary hooks so let's go ahead now and let's go outside of this div which is wrapping the h2 element and the verified mark and go ahead and write a paragraph here which is simply going to render the name of the stream right so that should now be do did we add the name we have the name in the types but it looks like I forgot to destructure it in the props so make sure you do that and now you should see the text which says Antonio's stream which is the default name of our stream if you remember in my LiveKit webhook when I create a not here sorry in my clerk webhook when we create a new user we create a stream with the default name of Antonio or whatever is the username Antonio's stream right so that's why that is appearing here so let's head back inside of our header component here and let's go ahead and give this paragraph which renders the stream's name a class name of text small and font semi-bold and now let's go ahead and dynamically render some content so if we are live in that case I want to go ahead and render a div with a class name, font, semi-bold, flex, gap x1, items center, text extra small and text rows 500.
And then inside I'm going to render the user icon from Lucid React. So make sure you add the user icon here, and I'm just going to move it to the top with the global imports. And let's go ahead and give this user icon a class name height4 and width4 and below that we're gonna add a paragraph which is going to render our participant count and then we're gonna change the label so if participant count is equal to one we're gonna say viewer otherwise it's gonna be viewers, right? So if it's 0, it's gonna be 0 viewers if it's 2, it's gonna be 2 viewers but if it's 1, it's gonna say 1 viewer so this should be in one line of course, like this Alright, and now what we have to do is we have to render an alternative for this ternary so let's write a paragraph here and I'm simply going to write offline. And in here let's write a class name font-semi-bold text-extra-small and text-muted-foreground like this.
Great, and Now let's go ahead and modify this user avatar is live to actually use our is live constant. So there we go. Now as you can see I am completely offline and this says offline. But if I go into my OBS and click start streaming this should all change and this should go from offline to one viewer. Great!
So it is working. So I'm going to stop streaming now and what I want to create now is the actions here so we can follow and unfollow this user. So for that we're going to go ahead just above this last div here add an actions component like this and go ahead and pass in the isFollowing to be isFollowing, pass in the hostIdentity to be hostIdentity and isHost to be isHost like this. Great! So now obviously we have this error here because we don't have actions defined so let's get inside of the stream player and create a new file actions.tsx.
Let's go ahead and mark this as use client and let's go ahead and let's create an interface actions props here. Let's pass in the host identity which is a string, is following which is a boolean, is host which is a boolean as well. Now let's go ahead and export const actions let's return a div actions and in here let's assign the props so actions props and host identity is following and is host like this great so what I want to go ahead and do now is replace this div with a button component from ./.uiButton and let's change this import to useComponents and while we are here let's go back to the header and let's import this actions from ./.actions so make sure you don't import this from anywhere else because we do have some components named actions inside of our navbars, right? Great! So now inside of these actions I want to go ahead and do the following.
I want to give this button an on click for now to be an empty arrow function. Then I want to go ahead and give it a variant of primary. I want to give it the size of small and a class name of width full and lg width auto like this. You can see how now it looks on mobile, right? And you can see how it looks on desktop.
Great. So now what I want to do is I want to add a couple of dynamic items here. So first, Did we mark this as use client? We did. Good.
Let's go ahead and add our useauth hook. So let's use useauth from clerk-next.js. So make sure you import this. And in here go ahead and extract the user ID like that so let's just make sure that we have this and we're gonna use this in a moment when we create our follow function right we already have that service we're just gonna go ahead and call it from here. But what I wanna do now is show the content for this action button.
So let's go ahead and render the heart icon from Lucid React. I'm just gonna move this to the top. And I wanna give this heart icon a class name which is going to be dynamic so open up Cn and make sure you import Cn from libutils like this and inside of here let's go ahead and first render h4, width 4 and margin right up to and then we're going to use the isFollowing prop to dynamically render some content. So if it's following it's going to be fill white, if it's not it's going to be fill none. Like this.
Great. And then let's go ahead and modify this label to again use is following unfollow or follow like this great So what I want to do now is I want to create a function called const handleFollow like this. Let's call it toggleFollow, right? And in here I'm first going to check if we have the user ID or not. So if we don't have the user ID, what we have to do is we have to redirect the user.
In order to do that, I want to go ahead and import useRouter from next slash navigation. And then in here I can get my router useRouter like this and I can simply go ahead and do router.push slash signIn like that and now let's check if we are the host so if and yeah don't forget to return this right so we need to break this function so make sure you do return right now let's go ahead and check if is host we can also return right the host cannot follow themselves and now let's go now let's go ahead and do if is following whoops is following unfollow else follow like this so what I'm going to do is I'm going to go ahead and import unfollow from actions follow and I'm also going to go ahead and import on unfollow like this. And I'm also gonna go ahead and import use transition from React. Now in here, I'm gonna go ahead and extract is pending and start transition from use transition and then in here I'm going to go ahead and create const handle follow to very simply be a start transition which is then gonna go ahead and do on follow and we have to pass in the host identity like this right and let's go ahead and add dot then and we will get the data so we can fire up our toast so make sure you import toast from sonar and in here we're going to do toast.success you are now following and let's go ahead and render the username which is going to be data.follower is it following I think?
Following.username like this and let's add .catch here which is just going to be toast error something went wrong like that so now we have the handle follow function so we can go ahead and pass it inside of this comment handle follow and then we have to create an equivalent unfollow so we can just copy handle follow and rename this to handle unfollow and let's change the action to use on unfollow and we're gonna write you have unfollowed the user like this and I believe the data following is exactly the same and now inside of handle inside of toggle follow let's call the handle unfollow like this great And now we can use the toggle follow to be on click here and we can add disable to be is pending like this. Great! But I believe we also want to disable this button if we are already following sorry if we are a host, right? So let's do a pipe is host like this and there we go now as you can see I cannot follow myself because I am a host perfect what I want to create now is a skeleton of this so it has its own loading state so let's go ahead and let's import skeleton from .UI skeleton on or in our case components skeleton and let me just align this like that and let's go ahead and let's do export const actions skeleton and return a skeleton component with a class name h10 with full and lg with 24 like this great so now we have that Perfect and now we can go back inside of the header component here where we render these actions and what I want to do is create a header skeleton so export const header skeleton here and let's go ahead and return and let's also import the skeleton, right?
So in here import skeleton from add slash components ui skeleton like this great and let's go ahead inside and let's return a div, which is gonna have the exact same class names as our div, so this one, right? You can literally copy the class names like this and go ahead and paste them here. And then inside also the same div so flex item center and gap x2 then let's render the user avatar skeleton so that's why it was very useful that we had that and we can also pass in the size for the skeleton great So make sure you import user avatar skeleton, which we defined somewhere in the beginning of our application, I believe when we were working on the sidebar. So we have the user avatar skeleton and now below it, I want to open up a div with a class name of space y2 and I want to go ahead and render the skeleton component with a class name h6 and width of 32 and below that I want to use h4 and width of 24 and outside of here I want to render the actions skeleton from dot slash actions which we just created like this there we go and now that we have the header skeleton we can go back inside of StreamPlayer so inside of StreamPlayer we should have an index component here and go inside of StreamPlayer skeleton and we left a comment to do add header skeleton and now we can do the header skeleton from .slash header so make sure you import header skeleton like that I'm gonna move it here and now we should have a nicer loading state there we go you can see how we now have header elements here and even on mobile it should be like this.
There we go, so it's fully supported on desktop and mobile. Great, so you wrapped up the header skeleton. What we have to do next is we have to create our stream information and our user bio box.