Building a Twitter Clone with Tailwind CSS and Next.js

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] so last week as i was prepping a video to kind of demonstrate this feature from twitter where new tweets load and you see this see new tweets button show up and you can click on it i ended up taking a little bit more time making my clone of the twitter ui so i thought it'd be fun to go ahead and rebuild this mobile twitter clone in next.js using tailwind css because i learned a few things along the way and i wanted to share those with you so without further ado i'm all coffeed up here got my dunder mifflin coffee cup and i think that means we can go ahead and get started so the next app right now is pretty bare bones we just have this home page that is returning hello and i've gone ahead and made the underscore app component which is the way you do nested layouts in next and the only thing it's doing is importing tailwind which we have set up with post css here and i have a few tweaks i've made to the default tailwind font sizes so if we come over and look we see our next app is running here it's just rendering the home page and so let's just go ahead and start by looking at twitter.com here and we'll just start from the top so this header and this footer are kind of persistent elements in this ui you know you scroll twitter but these are always here and then as you shift around they change a little bit but these really belong to the app layout component here so i'm just going to code here for a little bit and i'll slow it down and let you know any interesting tidbits kind of as they come up [Music] okay pretty simple here for the header just uh the profile picture so far latest tweets this is gonna be dynamic here this title you know as you switch these tabs you see it says notifications or tweets or messages but for now we'll just make it static and then we've got this border here on the bottom and so we have that in our clone as well now let's uh leave this for kind of the main component which is going to be the pages that change and then let's go ahead and work on this footer down here okay so one thing i'd like to point out here is how to make the footer kind of stick to the bottom and have our page component here just take up whatever space we have remaining in the height of the viewport so flex is a perfect way to do this conceptually here we have header and you know maybe this could be kind of main so we can go ahead and wrap this in a main component just so we can see header main and footer and main should really spread out and flex to fill up this space well we can use flexbox to do this all we need to do is come up to the parent make it a flex parent that is a flex direction of column and that's going to lay these three things out in a single column right here if we didn't have that it would lay them out horizontally but we want them vertically here so we use column and then we also want to give this parent a height this is one way to do it so that now our parent has a height of 812 here which you can see is the height of our viewport and so now all we need to do is tell this main component that it can grow using the flex one class now the flex1 class is from tailwind but we can see if we look at the css rule it applies it's giving us this flex grow value which is basically telling the whole flexbox hierarchy here okay this one can grow to fill the available space and there we can see now the footer is pushed down here but if i were to go ahead and grab this you know and change the height we can see the footer always stays to the bottom so that's pretty neat and i use that trick a whole bunch now i've just put these placeholder links in down here but i already have copied some of the icons from twitter so i'm going to bring those in and replace these with those [Music] okay so i've got these icons in the footer here now and again these i just copied the svgs straight from twitter the only thing i did was turn them into a little function component that takes a class name and applies that along with some other kind of base classes here now one interesting thing i learned was this about the stroke zero class if you look at these icons and you were to take this off by default um the stroke is thicker you can really notice the difference here um it looks a lot thicker and so i was confused about that when i was first comparing these you know if we make this active right right here and we were to look i was like you know i'm using exactly the same svg icon why is it thicker but uh stroke zero kind of did the trick here so that was uh something that i learned and i just went ahead and applied it to all the icons down here but then i passed through the class name so that uh from right here we can set the width and the height you know in a larger project these would be coming maybe from an icon library they'd be in a separate file but for now this works and so i just tweaked and played with the height and width i i'm using flex here and each link is a width one fourth and then the icons themselves take up with seven here with a little bit of padding just to tweak it a little bit closer to twitter.com so the next step here is to get the actual navigation working these are actually using different icons this was something else that i learned was that twitter uses different icons here when they're active and so i have all of those down here you can see we have home icon empty search empty so uh the next step is to basically toggle these based on the active state and so we actually need to wire up these four pages in our next app and then turn these into real links so that's what i'm going to do next [Music] all right i have the basic navigation working here i went ahead and imported link from next and wrapped each one of our a tags with a link and the appropriate ahref i also went ahead and created these four new pages or the 3d pages rather one for explore one for messages and one for notifications and so you do that just by creating these files right here and now we can click on these and we can see that the url is updating and the content the page content here is updating as well so the next step is to actually get this navigation uh updating in the sense that uh you know these nav icons know whether they're active or not and if they are we can go ahead and swap the icons and the color so the colors should be pretty straightforward these svgs can just take on the current color so if i were to say text blue 500 on this one and then text gray 500 on this one there we can kind of see our icons are taking the color of their container so i think that's going to be basically the strategy here but we need some way to differentiate which ones are active so i've already made a link and i'm just going to pull this in this is a link component that i'm using to wrap the next link so right now we're just using the the base link from next i wrote this component the first time i did this project which we can see imports the exact same link and renders you know passing through the href but it does a little bit of extra stuff here it pulls in the router and then it just finds the current path name and compares that to the href that's passed in and if it's the same it'll append the active class name prop right here to the list of classes otherwise it'll append the inactive class name so other than that it's doing basically exactly the same thing it's just also rendering an a tag for us so we can just do that all in one go so i'll go ahead and update the footer with the link from my other project and then show you how it works [Music] okay so i've wired up the home button here now this link that i made which uses this it actually renders out the children here as a function so we can see it invokes children and passes it the is active derived state here and that means when we're using it over here and again this is nothing that's coming from next this is just an api that i kind of made up we get to use that as active state inside of our template and so we can customize the contents of our link based on that state i like this pattern for doing stuff like this because you know otherwise you'd have to make kind of four hook calls for each one of these and then get maybe a link component and is active state you'd have to name them here it's local it's scoped as active as scope so we don't have to come up with a name for it like home is active and explorer is active and notifications is active render props is a really nice pattern here and we can just basically move the text color from our link right to our home icon here and so we say the class name is what it was before but if the route is active make it blue and if it's not make it gray so now when we click off home we see that it goes from blue to gray so we can do exactly the same thing for each one of these so now if we look we see we have uh all these nav icons turning blue and gray based on the active state but like i said uh if you look at twitter it's actually that the icons themselves change and so this is again where the flexibility of kind of the render props pattern comes in handy is that we actually just want to render a different icon so i think down here we have home icon and we have home icon empty so instead of rendering just the home icon with a different color i think we just want to say when we have the home icon it should be blue right and then [Music] that's only true if this link is active otherwise and we don't need an extra curly here otherwise we want to render home icon empty has a gray icon and so now we can see the icon is changing it's empty and the color is changing so again just uh the flexibility of these local render props this state that a scope to this block this chunk of template i think is really nice so let me just go ahead and do this for the rest of the links all right so now we have all of the various icons changing and the url is changing if we were to see this we can see everything's working back and forth so our routes are set up now and it's looking pretty good so one last thing i thought was cool was how we get this title to change right now it says latest tweets and that's kind of statically in our header right here but you know in twitter as you change it it changes dynamically and to start i'm just gonna have a simple title here that says you know notifications or messages or what have you and if you look at the next docs one way you'll see them sharing state between page components and this app wrapper is just by using static properties right on the component instance because this component that we're getting passed into my app is actually the component the static function component from our kind of pages directory here so if i were to come here right to home and say something like home dot header title is equal to latest tweets and then we come back to our app js component instead of just rendering latest tweets like this we could just say component dot header title and now we see it disappears because we're on messages but if we go back home we see latest tweets so we should be able to go and basically do this for messages give it a header title with messages notifications and explore we'll just call it you know search so this is just kind of a super simple way to have the page component effect state here in the app component so i just thought that was you know really interesting really simple i mean this works great um and you know we're just rendering here from component.header title so it's just a static it's just a static property on this function component we're not using react state or portals or context or anything to change this you know right in in this case this header information is known at build time it's all static and so i thought this was a really neat kind of simple solution to this problem okay so now that we've got kind of the app shell working what i want to do now is just get the home screen the actual feed of tweets working just enough to kind of show you this little flexbox tip that i learned that was really the point that i wanted to get to in this video because it was something i learned and i feel like it's just going to come up again and again so i want to make sure and share that with you before wrapping this video up so i'm just going to wire up some dummy data here for our twitter home feed and um fetch that using swr and just render some dummy data so that we can style it according to um the way it looks here on twitter.com so i'm gonna do that and then i'll come back and get you updated [Music] okay so i want to slow down here because this is kind of where things get tricky um these nested flex boxes always start to throw me for a loop it's easy to like lose track of the nesting for me anyways and so i just wanted to slow down here and kind of catch you up so we're just fetching uh some tweets from slash api tweets and once we get them we are mapping over them so each one of these divs here is a tweet go ahead and make this border a little bit lighter and so uh when i look at this tweet right here and again i'm just keeping it simple but if we just find something that's mostly text right and i'm not even worried about this little part right here but the main part of this when i look at this you know we have this outer div right here which has padding and it has the borders so we're going to need it to have the borders and then the padding on it as opposed to outside of it so that those borders go all the way to the edge right so that's kind of this outer div that i have right here and then when we look at this the main kind of breakdown i see and again just we're doing a little bit of simplification here but i see one wrapper that's flex right here which is going to be a flex row because we have one block for the image and then one block for the rest of the content so that's the first kind of way i would break this down is that this outer parent right here would be flex and it would basically is like a kind of a two column um flex system going on here where the image is not going to shrink it doesn't change at all it's always you know looks like about 40 pixels wide and then the content here is the rest of the space and you know as this changes and stuff you can kind of see that that's what is changing so that's how i would think about this and so right here we see div flex and these are the two parts the image here uh the profile picture i just you know found a width and height that looked good and made it rounded full and it looks about the right size here for these avatars a little bit bigger than what's in the header i gave it the source from the tweet and then just rendered a p tag right here so the next part of this uh which is kind of the tricky part is is this part right here so basically we have the flex parent we have the left part and then we have the right part so now we can make the right part and break this down even further right and this thing is going to be another flex parent and it's going to have this row and and this time it's going to be in a column right it's going to be flex call and where it's going to be this column right here laid out vertically with the name and the time stamp and everything then the substance of the tweet right here and then kind of the um the icons right here so it's boom boom and then boom so that's one two three in column so really we want this thing to be class name flex flex call and then this is gonna say you know uh sam celikov my name and then my handle and the handle is going to be a little bit lighter text gray 300 and the name is going to be wrapped in let's say a font bold something like that then we're gonna have the tweet and then we're gonna have um the icons so if we come take a look at this okay 300 looks a little bit too light and i think we can replace this with tweet.name and tweet.username and these also look a little bit thinner and then they have this dot and then they have the time stamp so let me work on that next okay so i've got some more of the details in here we just have kind of four spans making up this row of text right here it's just a p tag and we've got kind of a name and we've got the username and then we have this dot and then we have the timestamp and when i was originally doing this you know i was feeling pretty good about this i thought it looked all right it seemed pretty simple and i kind of went on but what i found out is when i made um different length names i ran into some issues so if i pop over here to my kind of mock server right now i'm just creating stuff using my name and handle but if i were to just uh create list tweet five and i just have this set up with faker data and this is one reason i really like doing this um using something like faker to generate lots of different kinds of data so that it highlights these edge cases in my uis so now you'll see already this is complicating things a little bit so the longer names are going to make our job a little bit tougher but this is where kind of the fun comes in so let's take a look really quick at how twitter handles uh people with longer names and how they basically deal with it what they decide to hide and what they decide to show so if we find someone here like patrick mahomes we see that um it basically groups the name and the handle and then you know it truncates it off and then it shows our middle dot and then it shows the timestamp so if we were to make this kind of responsive we can see it's basically truncating that part so that is the part of this element here that can shrink to not take up more room but it always stays on one line and then this part of this element never shrinks it is reserved the space is reserved for the dot and the time stamp so this this guy shouldn't be able to shrink it all just like this image over here doesn't shrink at all and this is really the flex child that should be able to shrink to accommodate for the varying space here so let me start building out this and then catch you up [Music] okay so i think i'm at a good spot where i can teach you kind of the main trick the main purpose of this video that i wanted to get to now the first thing that i did here was just go ahead and split these up into these three blocks you know we've got this header row right here of this contents that's what i'm calling the header row which is this p tag then we have the tweet text and then we have a third div that we're going to fill in with the actual icons so within this header row i went ahead and made this a flex parent right and that laid out these like this in a row because the default direction is row and then we have these three parts so we have the name and the username and then we have the dot and then we have the time and we could really put these conceptually kind of in their own thing as well and maybe even say something like we never want these to shrink so let's just say flex shrink zero right and then i added the truncate class to this span of text right here but you can see you know in this case is where it is too long even though this does have the truncate class which again is kind of a handy utility that sets overflow to hidden and white space to no wrap which should be enough to make it truncate or stop at least and then text overflow with the ellipsis to give it the treatment the dot dot treatment but we can see it's not working and i can still um it's just not truncating even though this is clearly overhanging right so i remember when i originally tried to do this and i just tried a lot of different things making it like smaller like width of zero and then just telling it to kind of flex out and um you can see that that kind of does something we see that the truncation here happening now and so this is that child so let's go ahead and come to the child here and add width 0 and flex 1. so you can start to see it has something to do with the width that the flex layout system is using to calculate here and this kind of works in this case but we can see it really screws up the rest of our cases here and especially when it's too short the name here is being truncated even though clearly it shouldn't be and if we come back in and look at twitter we can see what it should look like so after a lot of tinkering with this and trying to fix it at the child level what i eventually realized and what someone actually shared with me the solution was was that you have to go up to the parent so the reason this is overflowing here has to do with the parents so again at the root level we have this parent and then we have the image and then we have the tweet body and again we could kind of label this as if we wanted to as well tweet body and the avatar so at this level this is our first outermost flex parent and then a child and then a child and ideally this child should be able to shrink but it's not and the reason it's not is because how the browser treats text in particular text has uh this sort of intrinsic width and when it comes to laying out text in certain layouts if the width isn't specified text will always have this intrinsic width which is based on its content which will basically always win and so in our case flexbox here is going to respect the intrinsic width of this child and so in this case or in cases where it's too long it's kind of like this is the intrinsic width of the child it can't go below that and so when our parent right here is laying out the avatar and then the tweet body it's looking at the tweet body and saying hey you know what this thing goes out here to 270 and i can't shrink that uh on my own because text has an intrinsic width and i can't do anything about it unless you tell me to do something about it so all we have to do right here is come to the parent here and say you know what regardless of your children you do have a ability to go smaller and we can make you either with zero or in our case we can just make you a min with zero so if we add that look at that our ui just snaps into place and all of our truncation kind of takes effect and so really that is the trick and it's all about the fact that the parent here is a flex and we can actually see this if we were to remove this so that it breaks again and then remove flex from the parent so that the avatar here and the name were just kind of next to each other and then if we were to make this smaller check this out as soon as i make this small then it works so the truncation is actually working here it's being respected in normal layout but because of the way flexbox works when flexbox is making its calculations basically it's going to use that intrinsic width property of text to do it so that's why we need to come to this child right here and say min width of zero and that will affect the way the flex parent is laying this out and it says okay i can go ahead and shrink that child just like this you almost wish that this was a flexbox property uh something like flex min width because that would help us explain why this class is here it's really to help in the flexbox layout calculations but this is how how you do it for text and now this is looking pretty good it looks like our truncation is actually the wrong color so why don't we put a text cool gray on the truncate right here and then we'll just make sure the name is text cool gray 900 just like that and now our ellipses are the right color so this looks pretty good and you know we can come over to our mock data here let's go ahead and make 50 tweets and see how this looks and i think this is looking pretty good the time is over to the left on the shorter names but on the longer ones we're getting that nice truncation treatment and this looks pretty good it looks like we lost our our sticky footer here i think we just need to come back to our app page where we render our main component here and flex one this is spreading up uh to take up the space but we didn't say if you do take up more space go ahead and scroll so we're just gonna say overflow scroll right here and that should bring our footer back and now we have the feed and it looks great again we have no kind of extra overlap or overhang um we can go to pages with you know less content and the footer still sticks or if we went back and only had five tweets here this still renders just fine so uh this is definitely coming along you know with this flexbox stuff it's kind of like flexbox inception almost so sometimes i do find it helpful to use these little labels here but basically that is how i break these uis down where you look at the outside and you say okay what is moving and what's fixed you start from the outside and work your way in so let's go ahead and finish up this we have this icon bar here and if we pop over to twitter that is these icons just like this that's kind of the last row in our tweet card column and then we can wrap it up so i'll do that and then pull you back in [Music] all right so i think we're just about done um first thing i did was go ahead and pull in these icons which are just some svgs i copied from twitter and you know gave them kind of the same treatment where we use fill current and just give them an explicit width and height here and uh you know that way they can take on the color so let me go ahead and undo what i did up here because this is kind of where we left off so we have these and they get laid out like that we can color them we want them laid out in a row if we take a look at twitter we'll see they're in a row like this so that's just a normal flex and that should take care of that and then we actually want them to spread out to fill in all that space so that's going to be justify between for a flex row layout here so that puts them all on the edge and then distributes the space evenly and that's pretty nice and that almost worked but if you notice here for some of the smaller tweets they don't go all the way to the edge and in twitter they should always basically go all the way to the edge even if the tweet is shorter so again looking at our structure here these things are being spread all the way to the edges but if we look at the icon bar it's a child of this tweet body right so the tweet body in this case um earlier we were having trouble with it going too big well now it's too small and flexbox is just gonna say oh i only need you know 248 pixels to lay all this stuff out that's all i'm going to take and so this is where flex grow comes in we can come here and say you know what if you're smaller than what you need to be then why don't you go ahead and fill up the rest of the space so if we slap a flex 1 on that then that makes this child fill in all the space these two rows are fine but now the icons because we're using justify between will fill it up and so now the icons are very nicely laid out filling up the whole space there no matter if the tweet is long or short so that is looking pretty good and then the last thing here i just went ahead and made the dates dynamic so the date was already on the tweet and i'm just using these new functions from this library called date functions this is something i've started using instead of some other more you know big classic date libraries this one just gives you a few functions you can parse it as an iso and then format it for the month and day and that's all we need to do here and then we just get all the dates here dynamically and again they're being pulled in the right direction and they never shrink no matter you know how long they are if we were to do like a longer version of the date we can see you know our layout is working exactly as we intend it to the dot and the time never uh shrink we give them priority and then that lets the rest of the name and the handle shrink to make room so flexbox uh pretty powerful stuff but a little bit like flexbox inception here once you have uh lots of parents and children and then children who are parents again but um hopefully you learned something there and again this tip is really the thing that it took me a long time uh banging my head against the desk before realizing this and um understanding that the way flexbox treats text has to do with this notion of intrinsic width so sometimes you need to explicitly tell the child you can go below your intrinsic width you can go either to width 0 or min width 0. there's a good css tricks article on this as well with some more examples i'll link that as well i'll upload this and link it in github and i'll also throw it on to netlify and now i'm actually ready to make the video i wanted to make last week which was showing you how to make this feature on twitter where sometimes when you come over here and it's been a while this little button will show up i wonder if i can get it to actually show up but if you are on the page or you visit a tweet and then go back i see there it is we have this button right here that shows up it says see new tweets and so uh twitter does this so instead of just rendering out from under you and showing you the latest version right away they give you this little button and if you click on it it's going to take you to the top and load all the latest data so i wanted to kind of show how to make that functionality and now we have this nice twitter clone here uh that we can use to do just that so thanks for sticking with me through that hope you learned a thing or two and i'll see you in the next video you
Info
Channel: Sam Selikoff
Views: 17,569
Rating: undefined out of 5
Keywords:
Id: Ra5SUzeXOac
Channel Id: undefined
Length: 35min 8sec (2108 seconds)
Published: Sun Jul 26 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.