Code Walkthrough for Next.js 13 / Tailwind CSS / MySQL App!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today we're going to do a deep dive into my new website lerob.io that I've recently revamped with a brand new design and updated to use the latest next.js features and I want to show you some of the cool things that I built so I have my code up on the left here and on the right I have my new site so I'll just kind of give a quick demo overview of of what I'm looking at here and then I can kind of slowly explain things as we step through the code base so first off I have a new design that I'm pretty happy about it's pretty minimal it's pretty simplistic I wanted just to strip back everything to a very basic but Sleek design so I have my home page here it is static it has some content that's being pulled from Twitter and from GitHub and from my blog views it has a couple links and this content right here is actually using ISR so these numbers will update every at most every 60 seconds and we'll get into that more in a little bit then I have an about page pretty standard but again this is a static page so really fast to transition between those two pages because next.js gives you that single page app like feel even though you're in a multi-page app it's really fast to transition between those pages even though they're being rendered from the server so I've got an about page with some links to some of my socials I've got a Blog that has a link to all my different blog posts these are using MDX and they're render using server components in the app directory with the new app router in next.js and they pull the different blog views from planet scale my mySQL database and I use MDX to render some images and code blocks and all sorts of nice things here I'm using Tailwind for the stock styling and then if I go to guestbook this is a server rendered page so we're mixing a bunch of different types of rendering and data fetching strategies here which we're going to get into and we have some some lovely folks here who have left some comments on my site by logging in with GitHub and then leaving a message and I think I display like the first 50 or so here which is just fun it's a little throwback to the the uh the Retro way of the web or the guest books so so there's some you know there's some nice little touches here that I really like one is I'm using frame or motion here on the left for this really Sleek transition between the different pages and actually if I zoom in let's see how big I can get this without it looking absolutely horrid but well of course I have a hover State here but notice that the the background rectangle that is moving between the different active nav links not only does it change position but it also changes the width which is a nice little touch and it's got this spring motion animation on it to make it feel really fluid really natural but we're going to take a look at the code for that as well too uh and answer every question that y'all have so in my readme I have a list of all of the different tools I'm using of course I have an xjs play to scale next auth or now auth.js I'm using Purcell of course Tailwind CSS and Versa analytics for tracking my page views and and traffic analytics there's still some things that I want to do here as I continue working on the site but I figured you know what I'm just gonna ship it and we can improve uh improve in public and I'm using PM pm so let me open up the file explorer here where to begin let's begin with the config files so all of our our DOT files are our config files I'm using typescript this is basically the standard boilerplate typescript file that next.js gives you I don't have to change anything here really next year just just spits this out for me and it is set up to use the new plugin typescript plugin in the app directory with next.js which gives you some really helpful typing and autocompletes the only other thing that I've changed in here is that I'm using content layer which is the tool that allows me to have type safe access to my content in this instance my markdown files now I have another video on my channel where I interview the creator of content layer so if you want to learn more about that go check it out but it's very neat I had to add a couple things in here just to make typescript aware of the generated files actually jump to that next since we're on the topic here so in my content layer config file what I'm doing is essentially if I scroll down just a little bit I'm defining a source for my content so I have some markdown files and I want to use them in my application now there's a bunch of different ways that you can do this um I've I've had just vanilla markdown before I've had MDX I've used a CMS I've used sanity before ultimately I wanted to use content layer again and use MDX again because it's easier for beginners who clone my repository to not have to go set up a CMS it's nice when you can include a markdown file directly in the repo and you're already up and running so what this does is it says Hey There is a Content folder that's going to be where all the markdown files are we have a Blog document we're going to use some plugins so remark and re-hype there's a whole ecosystem of plugins to work with markdown uh we're going to add some nice things here like being able to highlight specific words we're going to Auto Link headings and all of this is just using third-party remark or re-hype libraries on the actual blog we get to Define What fields we care about so I have a title when it's published the summary the image all that good stuff and I say they're all going to be MDX files and I also put some computed Fields so I don't have to manually write these out content layer is going to generate these for me so I'm going to generate a slug and this is kind of fancy but fun I'm going to look through the entire document and I'm going to do a regex for a static tweet component and I'm going to pull out all the IDS for that now I'll show you how this works a little bit later but it's kind of cool it allows me to embed Tweets in my blog post for example I have this tweet here and I don't have to use the Twitter uh iframe that they give you um so we'll see if I continue to do this because Twitter API is adding pricing we don't know how expensive it'll be just yet hopefully it's cheap um we don't really know yet so that's content layer let me pop over to the content folder really quick which is where this is actually pulling the content from inside of your eyes have a bunch of MDX files and all of these MDX files essentially correlate to a specific blog post that's being pulled here on this index so inside of here I'm writing markdown essentially I have some metadata at the top of the file and then in some of these files I'm able to stitch together not only markdown as you might have written on GitHub or in other places I can also use react components that's the magic of MDX so I have a call out and I'm using this to render let me actually pull this up here where am I at um this is this call out component here so just a nice little way to add some special styling on top and add some unique components then basically the sky's the limit you can build whatever you want because you have the power of the full react component um so that is the content folder we'll get more into this when we talk about the actual rendering of the page but let's go back to the config files what else we got here I have Tailwind Tailwinds fantastic really the only change I've made here is that I'm now looking at the app directory for files that I want to make aware of my Tailwind classes I'm using a custom font which I'll show the code for this and another small fun thing here that you should know about I really like this future option in Tailwind which it only adds hover Styles when it's supported what that means is it strips away all of your hover styles on mobile devices because you can't hover on mobile you can only tap so that's a nice little feature that Tailwind bakes in post CSS is part of Tailwind I'm using pmpm as we talked about um that's pretty much it on the configuration files other than the next JS configuration itself so let's talk about that um first up I'm going to be using some images in my application and I'm able to configure them here by saying I want to use the latest formats like avif and webp and I'm going to allow list a specific URL of images to optimize from in this instance it is images from Twitter I am opting into using the app directory which will be stable soon I saw someone ask about that it's getting closer every day and we'll use some of the latest features with the new app router in this video I have some redirects right now that I'm just hard coding in here that I'm fetching dynamically I do want to move out of this because this is actually fetching all of my redirects that are being evaluated before the rest of my request has been processed I'd rather do it at the end of the quote unquote routing chain so that when my router in my application is checking for a path it's if and only if it's checked all of the past that it knows exists then it goes to the 404s or the redirects so I'll be making that change in the future I add some security headers this is nothing new if you've been following along I had this on my last site as well as well too and I have links in here to all the different security headers the MDM docs and what they mean mean this is just a nice to have not necessarily A must-have let's now dive into the top most thing in our directory tree here which is the app directory so the app directory is new it was released in next.js13 you can call this the app router for for simplicity's sake but inside this app directory everything is a react server component by default now this is great because it helps you send less JavaScript to the client side and it allows you to co-locate your files whether they're Pages or not pages and it has much better support for data fetching with async components all of these things we're going to talk about here so let's start with the entry point into the app directory which is the layout so this layout is our root layout because it is the one highest up in the tree of our application so let me skip down just a little bit here to the actual render the default exported component here of our root layout it accepts some react children and then it actually renders the HTML tag for our document so we have an HTML tag I am saying we're using the English language I'm adding this font that I'm using this custom font which is this one right here Kaizer very nice very nice font we have a body I have my sidebar component and the reason this is pulled out into a separate component is because this is a client component so this client component runs on the client side it's both pre-rendered on the server and then it sends JavaScript to the client side so that it can add interactivity similar to the way that next js12 1110 and other Frameworks work so this is kind of how things used to work and how you would your mental model would have worked in the past for adding interactive components The Helpful way here for me to think about it is server components are great for fetching data and constructing your Dom but when you want to add interactivity that's when you usually pull for a client component and I have my main tag I have my children and I render the page now this is all using Tailwind so the styles are all right here there isn't any other styling that I need to do if I scroll up a little bit I'm loading my font using next font and this is great because it helps me make sure that there's no layout shift when I load my page so as you can see no layout shift in this instance my font has been cached in the browser but even if it wasn't cached it has font display swap to ensure that the fault will always show and next file will automatically pre-load your self-hosted font files so essentially it has every single optimization possible to make your fonts load fast because not going to lie that stuff is tough getting your fonts to load perfectly is really hard so we've tried to put it all into one really easy way of handling this now we've got some fun stuff here that some of y'all might have seen on the the twitterverse which is the improved support for metadata inside of the app directory this is effectively like built-in SEO helpers so rather than having to use the next head API in the Pages directory or the intermediary head.js file for those of you who have explored the app directory a little bit we have this new support it's available on Canary now to Define metadata either static like you're seeing here or dynamic fetch from an API I have both in my application we're going to show both so there's some really cool stuff here one I'm defining a template so the default title is my name anything else appends Lee Robinson on the end so if I go to blog you see in the browser it's blog you know vertical bar Lee Robinson I set some robots tags here so that it can be crawled by crawlers and I can get all the max SEO juices um some Twitter OG image work my favicon and then some search engine verification tags this makes it really really easy to handle setting meta and Link tags or metadata inside of your application that go in the head of your document so that's the layout that's the first thing that gets rendered on the page that renders your shell the next thing is the actual page so in this instance app slash page.tsx this is my index route or my index page nothing will be shown on the screen unless you have a page file a page special file so this is the page um I'm going to again start down here rendering the actual function and then we can kind of work our way back up so inside of this home page you're going to see the first async component now this is this is actually mind blowing this is the mic drop this is the coolest thing in the app directory because it takes advantage of the latest react features being async components and server components I can just turn my function into an async function and I can just fetch data directly in the function now you might be thinking how what how's that possible it's possible because react is going to stop and it's going to say actually let's go fetch this stuff then we'll produce this HTML and return it from the server so you don't have to do get static props you don't have to do get server-side props there's no serialization of the result where you had to do some kind of workarounds It kind of just works and that's really great because it gives you a ton of flexibility in how you want to fetch data it doesn't have to be in the page it can actually be lower down in the tree in components as well too and there's some other niceties here that we're going to talk about so what's happening here is I am defining some variables I am fetching the GitHub Stars the tweets and my blog post views in parallel using promise.all which is the kind of the naive way of doing it and then I just have a try catch uh that I have a uh just some some basic HTML here an H1 a paragraph an image there's some interesting stuff here that I'm going to talk about uh one who might be wondering why I have functions for all this stuff and that's because I have a script that allows me to delete all of my content and scaffold out a brand new app with no personal information it makes it easy to clone this and get started I'll demo that later um I have an image and there's a nice thing here a good to know with the next image component the next image component helps you use images without having layout shift and automatically optimizing them but there's this priority prop that is important to know if you're using images that are going to be quote unquote above the fold so they're going to render uh in the first thing that you see on the page if you do that you want to use the priority prop because it's going to pre-load that image and make it load as fast as possible this image is being imported from a file and what's cool about this um you know in this instance it's in it's in another file here because I have that script I talk about but this Avatar is located in the app directory I don't have to put it in another directory I can co-locate it with the rest of my code and that's that's pretty cool I can directly import the image I can have a blur up placeholder as I'm ready and that's pretty much that then I have some more data here I have Twitter GitHub views icons and I have some string templates here bio a list of stuff and that's pretty much it one other small thing here a pattern that I like this is just personal opinion but I like putting the SVG icons into a separate file like this Arrow icon here the SPG inline SVG can take up a lot of space in your jsx so I think it looks a little more clean when you put this in this I have this components icons file so I can actually just pull that up it's it's just a bunch of icons that I have inline svgs here that I've kind of optimized using svgo to get the smaller file size and then I import them here okay so I want to talk a little bit about the metadata on this page as I showed the metadata in the layout which is for the entire application I also have page specific metadata so these objects get merged together and the highest priority one wins for example I overwrote the title and description here and in this case this is for the index route so it's just going to be Lee Robinson it's just going to be this description and I have some you know some OG information as well too so this essentially extends the base set of metadata that I've defined now I also have revalidate and if you've used ISR before you've seen this revalidate and I get static props similar idea here for this page I want to revalidate it at most every 60 seconds to go fetch new information about my tweet or blog or GitHub Stars count so on the right here I have uh a million something 419 views if I refresh you'll see that the ISR kicked in because I've had this I've had this page open for a while the data was refetched in the background the static page was swapped out and the views went up and the tweets or the Stars went up not the not the tweets I haven't tweeted since I've been on here um so that's the metadata and that's that's pretty much all I have to say on the index route uh I'll Bop over to the info file here that I talked about as well too just so you can see what this looks like effectively all I'm doing is defining my constants for the information that is specific to me my name my avatar my my quick sentence summary of me and my bio just so that I can import them and use them and then I have a script that essentially deletes all of this and replaces it with placeholder information when you clone the Repository okay so that's the index route what else do we got here well I mentioned I had this Avatar it's co-located that's awesome we already talked about layout and Page let's talk about the CSS setup actually going back into my layout I was importing some Global CSS and this is importing the Tailwind base and then I have a few small additional classes that I've added mostly to modify the Tailwind topography configuration Talent topography gives me some default styling for how I essentially render the content of my blog post so it gives me some defaults and then inside of here I've overwritten just a couple things you can use this apply syntax with Tailwind to directly apply these classes so I've changed the code I've changed some images that all in all what do I have here I have like 130 lines of just extra custom Styles other than that it is mostly just the stock tailwind and that's that's effectively all the styling in my application so that's pretty nice uh I also have an error file which I'll show in a second when we get to the guest book so let's go to about now I mentioned that in the app directory it doesn't render anything unless there's a page special file so in the Pages directory you would have page.tsx or about.tsx and that would be the literal name of the route in the app directory you have folders you can have really whatever configuration you want it's the page.tsx file that marks it as an addressable route so I have an about folder and then I have page.tsx and that is what makes this route available for me to navigate to so in here again I'm extending the default metadata and I'm saying I want about it's using the default template here to add my name on the end I render a basic static component there is no data fetching it's just some HTML and some Styles and reusing some of the same components nothing too fancy here other than um I'm using the native dark mode Style link for Tailwind so by prefixing styles with dark and then a colon it allows me to style things to differently depending on what the system theme is so if I go into raycast I can toggle my system appearance brace your eyes and I toggle to the system light theme which changes the color scheme of my application I'll toggle this back and go back to dark mode because dark mode is the best mode I think we can all agree on that and yeah that's pretty much it and about nothing too special here let's go to the blog now some things are going to start to get interesting we're going to start to talk about some of the more intricate parts of the application so first up we have the blog index page we have the the metadata again to extend the title in the description we have an async component here which actually I don't think needs to be async I think that was a remnant of something I was doing in the past so I could probably delete that I'm code reviewing myself as well here too while I'm while I'm going on this um I get all the blogs I sort them by the date they were published and then I map over them and I render a next link next link will allow you to prefetch the routes on the screen so that the navigation is really fast when you click between them and I render the title and then the view counter now there's something interesting here happening and it is one I'm importing all blogs from content layer now this is some of the magic that content layer happens for me or that it handles for me is that it will automatically generate all of my blog files or all of my content into this content layer folder I have all the generated things here into Data effectively a Json file here and then I'm able to just import directly from this folder without needing to kind of hook without needing to hook anything up myself so I can import all blogs and I can access them here and a nice thing is if I do a I have access this is fully typed I have access to all the properties that I've defined on my content model that's why I'm able to do published at and then we'll see it's a string title I'm able to see it's a string Etc so that's super nice um what else here what else here is interesting uh this view counter is interesting uh that actually just made me remember why this is a async component this view counter is a client components and effectively what this is doing is it is there's two different modes that this can run in it can either run in the track View mode or it can run in the display mode so in this instance I'm just displaying all the views and I don't want to track any views so when I reload the page notice how there's this flicker essentially where initially I showed nothing and I fetch the data from my API and I display it on the page now in this instance I'm using SWR so that it has the revalidation when I navigate back that might change as we make improvements in the app directory so stay tuned for that but at least for now let me go back to here I fetch all of the views I'm just using a basic Json fetcher I filter by whatever the slug is that I'm looking at right now remember this is being iterated over inside of this map so I'm able to pass in the slug as a prop and track view equals false it gets the views based on the response from this API and then I render out a paragraph It's just rendering this text if there's data render the views to local string will actually put the comma in there for me or I just render a space here now let me pull up API views you can see what this data actually looks like so inside of pages API views again API routes will move to the app directory what I'm doing here is I'm fetching data from planet scale from my mySQL database all I'm doing is essentially taking my query Builder I'm selecting from the views table I'm grabbing just the slug and the count from that table and then I'm running this statement and then I return the Json data now you might be wondering what is this what is this folky syntax that I'm looking at here and how does that work well let's take a slight detour to talk about planet scale so player scale is a mySQL database you could use whatever database you want my SQL postgres you can use redis it doesn't really matter I'm using it here because I enjoy it and I like to try it in Tech and in particular I'm using MySQL with uh keysli or kaisley I think it's keasly if I remember correctly I need to double check the the pronunciation of that but what it does is it allows me to have a very lightweight orm or query Builder that's compatible with both serverless and Edge compute so it works on versel and other platforms and it gives me type save access to my data so all it takes is I use the plant scale version I pass in the database URL I Define what my tables are I have a views table and I have a guestbook table and what the properties on those are and then what that gives me is some really helpful stuff so if I go back in here let's say I did this now it says hey by the way there is no table in your database called viewsd so that's not going to work and then even if I was in here if I typed this wrong again that's going to blow up in my face it's going to give me the red squigglies and it's going to say hey that's not something that you can actually select from your table so just a really simple way of getting typesafe access to my data again there's other ways of doing this quick plug for my blog post here 2023 state of databases for serverless and Edge everything you've ever wanted to know about all of these different solutions it's pretty long but there's every single solution listed in here that I am aware of or at least maybe not every single one but there's there's quite a few listed there okay let's go back to the view counter now alternatively I can pass in a prop to track a view and this needs to happen on the client side because you want to make sure the page actually loaded on the client side before I want to track the view if I track The View I'm going to hit an API and it's going to increment The View essentially so I'll just pull that up as well here it's very very similar I get the slug from the query I construct a query here and I check I select from views where the slug matches uh I'm gonna this is the the get so I'm going to get the views for this specific slug if it's a post to this route then I'm going to increment the count that's here and I'll return that new value otherwise if it's just to get to this route I'll just return the values back um so that's how I had this set up here which works decently well for example then if I navigate into let's see I'm at 8 000 540 here if I click in and I refresh the page you're going to see that I have the new value here of 541. um so that's the view counter it's a client component if I go back to my page I don't think there's anything else don't think there's anything else on the main root blog index route so next let's take a look at the individual blog post we talked about the main overview route but then if I go into the slug folder using a dynamic route I have another page and in this page I'm able to read the dynamic route I'm able to read this route parameter here and get the param dot slug and then I'm going to able to find all my blogs and filter down to just the one for this slug if I don't have a post I'm going to use this not found function which is going to take me to my 404 page then I fetch all my tweets so I've extracted out all the different tweet IDs from my post using that regect I talked about and get tweets is a little helper function I have here if I scroll up inside of this file that is it's kind of a hacky file to be honest um I would love to get rid of this in the future but basically it is calling the Twitter API directly it's doing some uh massaging of the data to get into format that I like and then returning it back so that I have the tweets that I want to use okay let's go into here now we get some interesting stuff one I'm using react text balancer so I import this or sorry react wrap balancer this is a amazing component created by Shu at Purcell it allows you to automatically balance out your titles so that they never have like that weird overhang down on the next line uh it's great super easy to use you just wrap your text in it and it just works so definitely worth checking that out I render when the post was published I render the views again I'm reusing the same exact view counter component that I did the last time but in this instance I am actually tracking the view so slight just a slight difference there uh I have MDX so this is a component where I pulled out the MDX rendering of my content I take in this is this is from content layer so I have the post on the post from content layer I can access the body and then get the actual code for the entire page and this is my little addition here too where I forward in all of the tweets I see a comment how are the views tracked yeah I use Planet scale for my database and I have this file here that effectively hits an API route to save the data to my database it just uses a user effect to call it API route so we're going to talk about the MDX here in a second but I also want to show you that Dynamic metadata that I talked about so you saw the static metadata but you might have been thinking well what about stuff that is dependent on the URL so in this instance we have a generate metadata function so I'm able to again I can access all blogs this is in the same file filter by the slug and then I can pull some information off of that blog post I'm going to have an OG image that's using the versell OG package to dynamically generate OG images based on my content and then I return a metadata object that looks it's the exact same object as the static option except it's fetched with Dynamic data now next.js is going to merge all these together and the highest priority ones win so again the title the description the images I've overwritten all of that with the latest stuff here which means that as you can see on the left here actually if I want to pull this up just so we can really take a look I'll pull up the Dev tools here I'll expand the head of this document and you see I have not only things like the Google site the index verification the robots all the stuff that's from the initial layout metadata but I also have the overwritten parts down below which are the title of this blog post the image for this blog post it all next uses handles all of that for me now there's something cool here which is my uh my OG cards it's a good time to talk about that so I'm able to dynamically generate OG images on the Fly depending on what my title of my blog post is so for example um I could say sup stream and now I have a brand new OG card that has my title on here I could even take this further if I wanted to I could you know change out the author name in this instance it's all me so it doesn't really matter but I can use this to really easily have to you know generate all these different things without needing a bunch of different files so I love that let's take a look at how that actually works I don't think there's anything else that I want to talk about here okay so I have an API route that I call OG and that's the route that I'm actually hitting and this is how it works it's very very cool very very cool I see some mind blown reactions in the chat yeah I really really love this this is something that um is uh it's something that we have a verse OG package for it works with nextges it also works with other Frameworks um and we're wanting to like build it more into next chest in the future so let me just walk through here I am using the edge runtime here which is important um you need to use the edge runtime for this it just it usually works better you want your um you want your OG images to respond quickly so that when the service goes to actually hit that URL it can pick it up very fast and show the image and like slack or on Twitter in places uh I am loading a custom font so I'm loading that same Kaizer font and then I have an API route to normal API route but it uses the edge runtime so I'm able to use the web apis for example getting the search params URL search params is a web API I'm able to get the title I fetch the font and then this is the versel OG magic it's really cool you write jsx you write like it's a react component so I have an image response and I return a new div and I just do some styling here you can also use Tailwind for this it's experimental but it's kind of cool in this instance I just went with the normal inline Styles so I have a background image I have um my title the post title and then I set the width and height of the image and the fonts that I want to load so 60 lines of code that's all it takes to generate my Dynamic OG images love that feature super helpful definitely recommend checking it out um what else there's only one other thing in this blog file that I want to talk about which is the dynamic routes so as you recall I have a dynamic route of slug here which means that I need to generate all the different versions of my page now this is an optional thing as in the Pages directory you have uh get static paths when you want to generate static versions of a bunch of different pages used get static paths to figure out which ones you wanted to build during the build and for the app directory it's called generate static params it's a little bit streamlined of an API similar idea but in this instance I want all my blog post to be static I want to generate them during the build so I'm iterating over all the blogs and I'm returning the Slugs I'm returning the slug because that is the value that is that matches one to one with the name of my dynamic param so we've talked about the blog we can close out of that chapter and let's go to the guest book things get a little interesting here because now we're doing server rendering which I'm very very excited for so let me pull up the code here I have the page uh in this instance I did the data fetching for my page uh actually in the file itself you could abstract this out if you want I just wanted to do it directly in here for the sake of putting everything in one file that kind of comes down to a stylistic thing so select from the guest book again if this is messed up I'm going to get the errors because it's gonna say by the way that's not a valid table guest book get the ID body created updated order it for me and then get the first 100. grab that data this is just an async function and then inside of my default exported react component for the page I use an async function again there's no serialization there's no gestatic props get server-side props you just mark it as async and then you just fetch your data that's it that's all it takes to fetch data there's nothing else that you need to do now in this one I am doing it the quote unquote correct way by using all settled so I Define two variables for the guestbook entries and then for my session I make two requests or two promises to get the data for the guest book which is what we just defined up top and the cool thing about this too is because there's no serialization I have type safety here even on the return from this function so for example if I did um let's see down here in entries and it's not entries I think it's similar somewhere else on here well yeah on the guestbook response it's telling me the options I have available from my database so that's kind of nice anyways um so I'm using promise.all settled and I'm checking basically if the if the request was successful if it was I set the value otherwise I log out an error and by the end of this block I now have the guestbook entries as well as if the user is logged in now let's talk about this git server session this is a new Improvement in next off now off.js that allows me to fetch the session inside of a server component so I use this function it used to be unstable it's now stable and then I pass excuse me then I pass in the auth options into this and that allows me to I'll just log out here for the demo I have this sign in with a button I click sign in I've already off with GitHub here so it's really quick and snappy but that would pop up a modal or a a new window that would allow me to log into GitHub and auth here so that checks to see whether I'm logged in so let's scroll down a little bit here is the actual content of the page so guestbook if there's a user if the user is logged in then I want to render the form as well as the sign out button otherwise render sign in which you kind of just saw the demo of those two states this is a server rendered page then take all the entries and render when it was created and what the what the person left in the guest book now let's talk about the form inside and sign out so this is all server component we're doing the data fetching we're checking the auth entirely on the server but there are pieces that require interactivity if we want those to be on the client so there's another question too can I put my components deeper down into my folders in the app directory or do they have to be somewhere else the answer is yes you can co-locate these components directly next to my page even though the only place I'm using these is inside of the page so first up I have actions this is pretty simple it's a client component and there's a button so it's either sign in or sign out and because it's interactive because I have an on click event handler that's why it's marked as a client component and next auth gives you these really helpful methods that allow you to Super quickly sign in sign out easy as that so sign out you click sign out sign in you click sign in but I specify that specifically in this instance I have set up the GitHub provider here that's that whole file form in this instance we are going to do a good old-fashioned HTML form we are working on improved support for mutations in the app directory with the app router that will make this even easier but at least for right now it's still possible so let's talk about how it works one I have a form that handles submitting I have an input that has this name for the entry I have a button that has Type submit to submit the form and I want to disable that button if I'm actually making the request and changing the data so I click on submit and it's going to call the on submit value so let's scroll up a little bit here I'm gonna say hey don't do the default where the page reloads the default browser behavior for forums I'm going to set some react State saying that I'm fetching grab the in input element for the entry get the value forward that to my guest book I'm just going to pass it in the body as a Json object and then I say you know what well I clear out the input look at this Arrowhead like this is amazing error handling I get it back and I do nothing with it because I haven't done that code yet maybe I'll add that in the future this is Peak personal site where I've decided to skimp on the air handling uh I say okay we're done fetching and then I start a transition so what we're doing here is we're using react transitions we're refreshing the route and we're getting new data from the server and we don't lose any of that client-side state so I do router refresh which is the essentially the work around right now when we don't have built-in mutation support that allows you to go fetch that new data and update the page so to demo here I'll just say like I'm showing this live on stream right now coming soon to YouTube as well now when I hit enter it's going to actually call this on submit the form will be disabled and it will also change the opacity so it's clear that it's disabled I won't be able to spam the button because the button becomes disabled and it's going to make this request and then the data updates without losing any of my client-side state or having to reload the page and I see the new data shown here so that's that's all of the form it's going to get a lot simpler in the future so I'm not too fixated on how it is right now but that's the guest book let's move on to the users page this one's easy it's literally just HTML uh it's it's just a list of stuff that I'm using I've got some some jsx some HTML and I'm using Tailwind typography again like in the blog which you use through this prose class name uh and that's that's everything on this page I also have metadata which extends the root layout metadata as well too what else I've already talked about everything in app let's jump down to components um I have for sale analytics which allows me to track page views for my application what I could actually do here is let me pull that up I'll just give a quick little demo in case you want to see what this looks like um it's kind of nice it's free right now in beta it will have a free tier in the future if you want to check it out and you know replace Google analytics or other similar tool let's see last 24 hours uh I'll go to here so here's my versal Analytics um it seems like a bunch of people visited uh 3 P.M I guess let me go back last 30 days a bunch of people visited on 10 000 on Monday January 30th so yeah visitors you have page views and then I can actually drill down here into like where they're coming from and who the referrers are and where people are visiting from for sale analytics it's great I can also check web vitals as well too um which is pretty nice yeah it's pretty cool let's talk about the MDX components that we have and then I'll talk about the sidebar and the Tweet as well too so in the blog essentially what I'm doing if I'll scroll way down to the bottom here is I have this component that takes all of the MDX components that I want to use so I have a bunch defined in this file as well as the static tweet that I'm defining in line here because I passed in the tweets so if you remember I fetched my tweets on the server and I'm filtering them down here to find just the Tweet with that ID and then I use this tweet component but essentially all I'm doing here is I'm telling my MDX provider I'm saying hey when you go to render and H1 when you go to render an H2 when you go to render a code component these are the components that you should use so I have a few custom ones defined I have a custom link that uses next link for those fast transitions otherwise it adds the Rel no opener Target blank so that it opens in a new tag a new tab I wrap all my images in next image and I make them rounded with a class name I talked about the call out a little bit but just a nice little call out component here I have a pros and cons card which is just you know something a little fun to add some checks inside of here and that's yeah that's that's all the components I have other than the default styling that Tailwind topography gives me so nothing too uh nothing too extravagant here uh other than the tweets which I mentioned I fetched the tweets from the API directly I have a video on this on my YouTube channel if you want to learn more I'm not going to go into this in depth but basically I'm just building my own tweet component so it's a bunch of uh jsx and images that takes the data from the Twitter API and it actually renders out a tweet so for example um like this or like this with images or like this you can handle quote tweets as well too yeah it works pretty well so there's a video on my channel if you want to learn more about that let's talk about the sidebar I know there was questions on the sidebar how do I get this animation which is different if I'm on web or mobile so I'm on kind of a mobile viewport here and it's a horizontal transition so notice how it jumps between these but if I'm on this viewport it's vertical how do I do that how is that possible well my sidebar component here is the client component again because of the interactivity and it's using frame or motion so what I did is I defined the different nav items what their name is what their X and Y position are and what the width of the box that I want in the background is so if I scroll down I'll skip a little bit of this here I'll go to the main export I check for the path name use path name is a hook that's available in the app directory for client components so you can look at the path and I added this check so that any blog post will make the blog box active if I scroll down a little bit here's the magic for all the different NAB items that I defined in my array given the path name what I want to do is I have two different versions I have the let me make this a little bit bigger if it's the death desktop version it will be hidden on mobile this is the one that animates the y-axis so I have a motion.div which uses frame remotion and I said a couple different things I set the initial X and Y positions I say where I want to animate to I want to change the opacity from 0 to 1 and I want to do that spring style animation to make it feel really fluid so that's what you're seeing here when you navigate between these two and I do the same thing but for mobile except I invert the positions because it's going to be a mobile it's going to be horizontal instead of vertical now you might be wondering why do I have two of the exact same element uh instead of just doing some kind of conditional and that's because it's kind of hard to do a conditional rendering of this when it's a completely different orientation uh so what I did here that I find a good solution is that I just use CSS to hide the element um on mobile or on desktop for the different div that I'm trying to show I feel like that's easier than doing some kind of JavaScript to check whether I want to do mobile or desktop or looking at the viewport width it just feels a little bit cleaner to me then I also iterate over the nav items here and I use next link to do all the paths and I also have the nice little active animations here as well too that allows me to do the this when I hover over them the only other thing to mention here is I also animate my my logo here it uses motion.spg to animate the two different paths so there's the L Bar the top bar the like the side bottom part and I'm essentially just animating both the opacity and the length changing their values just a little bit so just small little touches that I I like just to add a little bit of extra Flair I needed an excuse to play with framer motion basically and that landed me here with this sidebar I'm I'm pretty happy with where I landed I think it's it's a decent compromise for where I'm at okay we talked about app components content Library um we haven't talked about library or Pages yet in depth so Library we talked about Twitter talked about playing a scale metrics this is the data that's being used on my home page all this is is effectively a couple promises and I am doing something really interesting here which is I'm using cash now this cache function allows me to Cache the result of my API such that if I make multiple requests to it it's just going to reuse the last one so I'm fetching the data from my blog here and I'm accumulating the number of views and I'm wrapping it in Cache now react also has built-in fetch caching that's experimental right now which means that if I am not doing something custom like a query Builder here and I can use the web fetch API directly it will automatically cache this response for me unless I specified otherwise using a cache control header so for example here I'm fetching the Tweet count I return that the result here from the fetch will be automatically cached by react I don't have to do anything unless I want to change that to have ISR semantics or to have server rendering semantics and I do the same thing with the star count in this instance I am using the GitHub octa-kit API here to make that or SDK to make that a little bit easier uh there was a question why do I return zero when the API token isn't set um this is basically to make it easy for people who clone and deploy this who maybe they don't have the Twitter API set up I just don't want the build to fail if it's trying to uh you know make a make a request to something that's not legit oh I see what you're saying yeah this is a typo this should be database URL and not Twitter API token again that live code review that live debugging really really helps so thanks thanks for the call out here another cool thing you mentioned import server only what the heck is this thing well this is effectively quote unquote poisoning That's the the technical word poisoning this file to say it can only ever run on the server if you try to import this or use it anywhere on the client it's going to fail this is really powerful because that means that you can very confidently know that you can use environment variables inside of here that have secrets and they're never going to be exposed to the client side and you could package this up as a reusable Library and make the API key a prop you pass in or an argument you pass in and be very confident that the contents of this library or this file would only ever run on the server very very powerful I'm guessing we're going to see a ton of new abstractions built on top of this from companies building their sdks their ways to interact with their clients so that's very cool the only other thing to mention here is this setup script which is kind of funny uh actually what I can do here just for fun what I will do is inside my package Json I have an npm script of run setup this is going to run this node script that runs the setup script that I have so let me do this and I'll just show you what I'm working with so I have a public folder that has all the images from my blog this is mostly uh uh uh just moving things over from the way they worked last time I could go like co-locate if I wanted to I have images I have this info about me in info let's say I want to go in here and run the setup script what's this going to look like well first off it deleted all my images second it deleted all of my blog posts inside of content and look at info so we have some some placeholder info here now your name play solar Avatar so if I actually start this up on my local Dev server I also have a placeholder blog post so now um let's see I have another application running on well close three thousand too many things running 3001 is where this is at so I see your name I'm a developer I have a placeholder Avatar I I have some inverse sets so it's able to fetch that stuff if I go to blog Hello World I see this brand new post and that's pretty cool that makes it really easy for me to get started and kind of delete all all the stuff that I had included that was my own personal information makes it really easy for folks to kind of get up and running and get started with my repository that wraps it up for taking a look at my code repository for lyrob.io the source code for this is available on GitHub I'll link it in the description as well too if you want to go check out the code if you want to clone it and deploy it I'll also leave a link to that as well thanks for watching thanks for tuning in and see you in the next one peace
Info
Channel: Lee Robinson
Views: 34,595
Rating: undefined out of 5
Keywords:
Id: jeBy4vIBqw0
Channel Id: undefined
Length: 59min 53sec (3593 seconds)
Published: Mon Feb 06 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.