Sam Selikoff: Improving developer and user experience with nested layouts in Next.js

Sam Selikoff: Improving developer and user experience with nested layouts in Next.js


Sam Selikoff: Improving developer and user experience with nested layouts in Next.js

Get an inside look at how nested layouts with Next.js 13 can improve your user and developer experience with Sam Selikoff, Founder of Build UI.

Deploy now: https://vercel.com/home


Content

0.719 -> foreign
5.21 -> [Music]
9.3 -> hi there my name is Sam selikoff and I'm
12.24 -> super excited to be talking to you about
14.34 -> one of my favorite features coming to
15.96 -> next 13 nested layouts I've used nested
19.56 -> layouts in a lot of different Frameworks
21.84 -> going back to my time in Ember and I
24.3 -> love the power they give you to build
26.34 -> uis for the web and today we're going to
28.92 -> see exactly how they work in next 13 by
32.04 -> looking at this demo app so this is a
35.88 -> brand new Next 13 app we're going to see
38.34 -> we have this root app folder with two
40.559 -> files layout.js and page.js and here is
45.059 -> the root layout so the first thing
46.8 -> you'll notice about this layout is that
49.26 -> it renders the HTML head and body tag so
52.14 -> it's actually rendering the entire
53.76 -> document
55.199 -> and the second thing you'll notice is
57.3 -> this children prop which it's slotting
59.64 -> in right here inside of the body tag and
62.039 -> that's what's rendering our page
63.6 -> component
64.68 -> so uh right now we have one page right
66.9 -> at the root URL let's go ahead and make
69.18 -> a second page with movies
72 -> hey hjs
73.799 -> and we'll export a default function
76.979 -> and let's return a tag that says the
79.619 -> movies page
82.08 -> so now if we come over and visit movies
84.119 -> look at that we've got a new URL in our
87.479 -> app
88.619 -> so this is how we create pages in next
91.2 -> 13. and if we come back to the layout
94.979 -> let's come within our body tag
98.1 -> and we'll add a header right here
101.88 -> and now if we look at the movies page or
104.4 -> the home page we're going to see that
106.079 -> header text on both pages so in next 13
110.119 -> layouts are a first class concept we can
112.979 -> see here the header is being rendered
115.259 -> above both pages right above this
117.479 -> children right here so this makes
118.86 -> layouts a great place for persistent nav
121.82 -> like some links so let's go ahead and
124.259 -> add a nav tag
126.18 -> we'll add a home link
128.64 -> and we'll add a link to slash movies
133.5 -> and now we can see both of these links
135.66 -> are showing up and when we click them we
138.239 -> can see the pages being rendered
140.76 -> now just like in next 12 we have a
142.92 -> special component called link so let's
144.78 -> go ahead and change these to use that
147.78 -> and now when we click these
150.54 -> we're getting nice client-side
152.04 -> Transitions and some other Behavior like
154.26 -> pre-fetching that we already know and
156.18 -> love from next 12. so as you can see
158.76 -> this layout is the perfect place for our
160.98 -> header and it's also a great place to do
163.44 -> some Global stuff like importing
165.62 -> Tailwind CSS which we can do right up
168.78 -> here
169.8 -> we can see that taking effect and adding
172.56 -> some Global Styles like BG gray 900
175.26 -> let's make this a dark app and we'll add
178.08 -> text Gray 100 for the text and let's
181.379 -> just throw anti-aliased right here
184.98 -> and now we can come down and add some
187.56 -> space between these links
190.319 -> maybe add a bottom border to our header
193.86 -> with some padding
195.599 -> and we'll also add
197.819 -> one more div around kind of the main
200.34 -> panel of our layout here so we're off to
202.98 -> a good start but it'd be nice to know
205.2 -> which one of these links are active when
207.12 -> we actually click on them maybe we could
209.34 -> toggle some classes on this link tag
211.86 -> well uh next 13 has a great hook that
214.98 -> lets us do just that it's called use
218.04 -> selected layout segment from next
220.98 -> navigation here
222.36 -> and uh if we go ahead and save this
225.239 -> we're actually going to see an error and
228.18 -> if we come over and open the docs for
231.12 -> you selected layout segment we're going
233.28 -> to see that this is a hook that allows
235.86 -> us to read the current route segment
237.299 -> inside of a client component and so it
240.959 -> turns out this whole time we've actually
242.879 -> been working with react server
244.44 -> components they are the default
246.12 -> component type in next 13. and if I go
249.599 -> ahead and comment this out
252.299 -> and drop a console log for rendering
256.739 -> and we come back and refresh our app you
259.799 -> might be surprised to see that this
261.419 -> doesn't show up here and in fact if we
263.58 -> pop open our terminal
265.74 -> which is running our Dev server we see
268.68 -> that console log right there so our next
270.96 -> Dev server is actually rendering these
273.419 -> server components at request time on the
275.759 -> server and then sending their output up
278.1 -> to the client and this is different from
280.68 -> how server rendering Works in next today
283.759 -> which is pre-rendering client components
286.74 -> sending up the HTML but then also
289.62 -> setting up all the JavaScript so those
291.54 -> client components can be rehydrated on
293.52 -> the client and then they start running
295.199 -> and executing there server components
297.72 -> don't do that they just run on the
299.759 -> server and they just send their static
301.919 -> output up to the browser and this helps
304.8 -> keep the static parts of our app really
307.56 -> fast and it helps us send less
310.44 -> JavaScript to the front end as well but
313.02 -> when we do want the full power of react
315.18 -> on the client and uh want to be able to
318.06 -> add some interactivity like we do right
319.8 -> here to be able to tell which link is
322.139 -> active we want to opt into using a
324.539 -> client component and so the way we're
326.82 -> going to do that
328.44 -> is by making ourselves a brand new
330.419 -> component let's call it navlink.js
334.62 -> and nav link is going to be rendered
337.139 -> right here
340.259 -> so we will export default function nav
343.38 -> link
346.56 -> and we want to return
349.86 -> a link from next.js
352.979 -> and we want to go ahead and take the
354.72 -> href and children as props
361.74 -> just like that
365.22 -> so now if we import this
368.82 -> and get rid of these
371.639 -> let's save this
374.4 -> come to our nav link and say log href
379.68 -> so in next 13 we can make new components
382.199 -> anywhere we don't have to put them in a
383.699 -> components directory but if we refresh
386.16 -> this we're still going to see actually
387.84 -> all these logs happening on the server
390 -> and that's because any component under
392.16 -> app is a server component by default but
394.74 -> we can turn this into a client component
396.6 -> with use client
398.819 -> and check this out now when I save this
401.1 -> and we refresh we see The pre-rendering
403.74 -> Happening Here we see our server
405.72 -> component rendering but we're also
407.819 -> seeing these components being rendered
409.919 -> on the client and that's exactly what we
411.84 -> need to be able to use selected layout
414.36 -> segment
416.52 -> so uh let's grab this segment from this
418.88 -> and let's go ahead
422.759 -> and log these two
424.5 -> and uh we'll refresh right here on the
426.66 -> home page
428.1 -> and we'll see that the the selected
430.56 -> segment for the home page is just the
432.12 -> empty string for slash movies it's
434.34 -> movies so we should be able to see if
436.62 -> these are active
440.639 -> if the href that we pass in is equal to
445.319 -> our segment with a slash in front of it
448.979 -> so now if we log href and active
454.68 -> and uh now we'll see when we're on the
456.66 -> home page active is true for that ahref
458.4 -> and when we click movies uh active is
461.16 -> true for this one so now we can just
463.56 -> come here and do good old react things
465.66 -> like setting a conditional class name
468.9 -> of underline if we're active
472.08 -> and check that out
475.62 -> pretty cool right
477.78 -> so we have this nav link component which
480.66 -> is using react on the client to toggle
482.699 -> this class name but it's being rendered
485.099 -> by our layout which is still rendering
487.8 -> on the server it's still a react server
489.599 -> component and this interleaving of
492.66 -> server and client components is really a
495.66 -> key part of next 13's architecture it
498.419 -> gives us the full power of react
499.86 -> whenever we need it and only at the
501.72 -> component level but it keeps the static
504.3 -> parts of our app rendering on the server
506.639 -> and I just love how seamless it is to
509.039 -> move between the two
511.199 -> okay uh let's fetch some data we have
514.14 -> this movies route and we actually want
516.3 -> to display some movies here and over on
519.36 -> localhost 3001 I have an API running
523.02 -> with a few movies so uh let's come to
527.1 -> our movies page and fetch those movies
531.12 -> and we're going to do that using an
533.519 -> async function that we're going to call
535.44 -> get movies
537.899 -> so uh let's go ahead and await fetch to
541.68 -> that API endpoint
543.959 -> and let's return response.json
547.98 -> and to get this data into our component
550.68 -> we are going to import something called
553.38 -> experimental use from react and we're
556.56 -> going to give it an alias of use now you
559.8 -> might have heard of suspense for data
561.54 -> fetching and how it hasn't been ready
562.92 -> for a while well this is it and let me
565.74 -> show you how it works
567.959 -> I come right into our component
569.94 -> and we're going to call use and we're
571.62 -> going to pass it our get movies function
574.019 -> which we'll go ahead and invoke
576.66 -> so uh this returns a promise
579.6 -> and we're just going to assign this to a
581.339 -> variable called movies and let's go
583.62 -> ahead and log movies right here
587.1 -> and if I pop open our terminal we're
589.56 -> going to see our movies are logged right
592.32 -> here so we can go ahead
595.92 -> create a list
598.98 -> and movies.map each movie
602.76 -> to An Li with a key of movie ID and
607.14 -> let's go ahead and render the title of
609.18 -> each movie
611.7 -> and there we go now we see the movies uh
614.64 -> in our app
616.8 -> so uh this is really cool and it's
618.72 -> really the first time that react has
621.18 -> given us a first class API uh to fetch
624.839 -> data within a component and basically a
627.36 -> way to tell other components that this
629.64 -> component is currently fetching data via
632.279 -> suspense and that's how this is all
634.26 -> working in fact if we go to the home
636.48 -> page
637.56 -> and we open up the dev tools and slow
641.7 -> this down to a slow 3G throttle
644.94 -> uh if I click on movies
647.399 -> we're actually going to see that the app
649.079 -> pauses and it doesn't navigate until all
651.899 -> the data is ready and again react has
654.6 -> never been able to do this on its own
656.519 -> it's always needed a framework to
658.86 -> provide a framework specific hook like
660.839 -> get server side props to have this kind
663.48 -> of traditional request response Behavior
665.88 -> but uh now through the power of use and
668.88 -> suspense pages are able to communicate
671.7 -> that they're fetching data and they're
673.8 -> not ready to be rendered so having this
676.5 -> as part of react is going to really
679.74 -> improve the composability story across
682.8 -> different libraries and Frameworks
684.48 -> because every framework no longer has to
686.94 -> come up with its own apis for fetching
689.339 -> data and doing things like rendering
691.32 -> loading and error States and speaking of
693.959 -> loading States it was a little weird how
696.12 -> when we clicked on this on slow 3G it
699 -> took a while for it to load so let's go
701.64 -> back online
703.92 -> come here and we can fix that if we come
707.399 -> here and right next to this layout we
710.399 -> drop in a loading.js
712.98 -> and let's go ahead and Export a loading
715.62 -> component
717.12 -> and just return loading message right
719.459 -> here
721.44 -> so now if I go back home and we refresh
726 -> and go back down to slow 3G
728.88 -> let's click on movies
731.16 -> and look at that we see an instant
733.44 -> loading template that renders while our
735.839 -> data is being fetched so uh this is
738.779 -> pretty awesome we basically have this
741.66 -> way to go between the pattern where we
744.66 -> instantly navigate and show a loading
746.399 -> spinner or we do kind of the traditional
749.04 -> model where we click a link we wait for
751.74 -> the data to be fetched and then we
753.66 -> render the new page and we can do either
756.12 -> one of those approaches to navigation
757.8 -> without changing anything about how our
760.26 -> page fetches data thanks to use and
763.32 -> thanks to suspense
765.36 -> okay now we're ready to actually uh make
767.7 -> these movies link somewhere so uh we
770.639 -> want to come here add a div
773.279 -> and uh let's kind of make a flex
776.1 -> style layout
778.2 -> where we have the movie detail kind of
781.32 -> right here
783.12 -> to the right so let's go ahead and add
785.88 -> some padding right to this maybe we'll
788.76 -> make the text small
790.38 -> and now we want these movies to actually
793.2 -> link to you know slash movie slash one
795.959 -> slash movie slash two where we can see
798 -> some more information about the movie
801 -> and this looks a lot like another layout
804.12 -> where our movies are kind of in the
805.86 -> sidebar and the detail is here in this
807.959 -> main pane which gets swapped out so uh
811.2 -> let's go ahead and turn this page
812.94 -> directly into a layout just by renaming
815.82 -> it to layout
817.56 -> and uh layouts get a children prop
822 -> which we can render right here to this
824.279 -> div
826.8 -> so if we save this and refresh we're
830.279 -> going to see a 404 because we're trying
832.2 -> to render slash movies and we no longer
833.94 -> have a page defined so let's go ahead
836.1 -> and create a page to be slotted in to
838.92 -> that children's slot
840.72 -> we'll say page return
843.959 -> movie page
846 -> and uh now we can see that right here so
848.94 -> this page is kind of the index segment
851.579 -> the default segment for movies so maybe
854.1 -> we make it say something like select a
856.44 -> film
858.6 -> and uh now we want to make these uh
861 -> movies right here in the layout links so
864.54 -> we'll wrap these in link which we can
867.42 -> import from next link
869.82 -> and we want the href to be slash movies
872.76 -> slash movie ID
877.079 -> so now we come and look we see that uh
879.779 -> this is going to slash movies slash one
882.24 -> and uh this is movie slash two we don't
885.36 -> have Pages for these yet but because the
887.699 -> URL has a dynamic uh parameter here this
891.3 -> is the perfect use case for a route with
894.54 -> a dynamic segment and we can create a
896.88 -> dynamic segment with brackets just like
899.519 -> in next 12. so we'll call this bracket
902.22 -> ID and then we'll drop in a new page
904.56 -> here which is going to be
907.68 -> a component as well
910.44 -> movie detail
913.26 -> so uh now we have this page being
915.48 -> rendered but we want to know which
917.339 -> movies being rendered Pages under
919.38 -> Dynamic segments here will get the
921.66 -> params in right as a prop so if we log
925.079 -> in prams there we see id2
927.98 -> id1 and so we can say movie params.id
932.04 -> just like that
934.86 -> and let's go ahead and make this text
937.32 -> 3XL
939.54 -> so uh look at that pretty cool we've got
942.06 -> our home route we've got our movies
944.16 -> route and uh this kind of renders the
946.98 -> index and now we can render these movies
950.24 -> and this is kind of our first level of
953.76 -> nesting going on here where we have this
956.16 -> root layout and then we have this nested
958.5 -> layout keeps things really nice and tidy
960.839 -> and only re-renders the portions of our
962.88 -> page that change
964.8 -> now for this detail page we actually
966.839 -> want to fetch the details of each movie
969.48 -> so uh let's come and grab our async
972.12 -> function from our layout and we're going
975.18 -> to turn this into a data fetching page
977.76 -> but this time we're just going to grab a
979.5 -> single movie
980.639 -> which we can do by hitting movies slash
983.94 -> ID just like this so if I come over to
987.66 -> my API and go to slash one there we'll
991.139 -> see Lord of the Rings so we'll make our
993.839 -> async function take in an ID
997.32 -> and then let's come and grab
999.36 -> experimental use
1002.12 -> and uh we'll get the movie by using get
1005.72 -> movie which takes in an ID and we can
1009.199 -> get this right from params
1011.72 -> so let's go ahead and log the movie
1014.839 -> there we see Lord of the Rings and there
1017.6 -> we see Star Wars
1020.959 -> so let's use this data right here and
1023.36 -> render the movie title
1026.299 -> pretty cool Lord of the Rings and Star
1028.52 -> Wars
1029.9 -> let's add one more wrapping div
1032.54 -> paste in uh the year and description
1035.959 -> and let's come back to our layout
1038.12 -> and we'll add flex and none to this
1040.459 -> unordered list so that it doesn't shrink
1042.319 -> here
1043.1 -> and uh look at this
1045.079 -> pretty cool little nested UI we have
1047.959 -> we can go home we can go back and uh if
1051.5 -> we take a look at what we've built each
1054.38 -> layout and page is kind of isolated
1056.9 -> right we've got our detail page here
1059.299 -> which is fetching data and rendering it
1061.22 -> our layout is fetching data and
1063.2 -> rendering it and then we have this root
1066.02 -> layout right here so the nesting that's
1068 -> going on here
1069.38 -> kind of starts on the outside with the
1071.9 -> root layout that renders a segment right
1074.66 -> here and then we have another layout
1077.059 -> here that renders the sidebar and then
1079.52 -> the current segment right here and
1081.86 -> everything is kind of nice and tidy all
1085.28 -> the pieces that are coupled together are
1087.14 -> in the same file so we don't have to go
1089.6 -> to the top of a route and make sure we
1091.7 -> fetch all the data for every component
1093.559 -> that needs it we can just look at this
1095.96 -> kind of leaf page right here in our
1097.82 -> system see that it needs a movie and it
1101.12 -> can fetch it right here and it can use
1103.28 -> it right in the component so from a DX
1105.799 -> perspective this is awesome because uh
1108.62 -> it really keeps things nice and coupled
1110.419 -> together the things that are related to
1111.919 -> each other and uh not only is having
1114.08 -> these fetch calls associated with the
1116.179 -> components that need them good for DX it
1119.24 -> actually opens the door for a lot of
1120.86 -> optimizations on both the react and the
1123.86 -> next JS side if we have more fetch calls
1127.7 -> associated with each dynamic segment of
1130.34 -> our UI they're easier to Cache across
1132.919 -> different pages also having the fetch
1135.559 -> calls near the components that need them
1137.36 -> will prevent over fetching because if
1139.94 -> you have to Marshal all the data that
1141.86 -> you might ever need it becomes easy to
1144.26 -> over fetch data and finally next we'll
1147.14 -> deduplicate and parallelize these fetch
1150.14 -> requests so we don't have to worry about
1152.84 -> fetching too many times or too much data
1155.059 -> even though they're co-located with
1157.7 -> these Dynamic segments so overall this
1160.34 -> is pretty awesome from both a DX and a
1163.1 -> ux perspective but you might be
1165.02 -> wondering with all these calls to fetch
1167.44 -> have we lost some of the coolest
1169.52 -> features of next things like get static
1172.16 -> props and incremental static
1174.08 -> regeneration let's take a quick look at
1175.82 -> what happens when we build our app
1178.16 -> I'm going to come down here and this
1181.4 -> terminal that's running my API server
1183.14 -> I'll kill that so that I can run it kind
1185.72 -> of split right here and PM run API
1189.5 -> and let's just clear this
1192.38 -> and here's our next Dev server I'll kill
1194.66 -> that
1195.74 -> and let's run npm run build
1205.94 -> now uh look at that we're going to see
1207.799 -> our API server was hit during our build
1210.679 -> and up here it said we're collecting
1212.78 -> page data and generating some static
1215.419 -> Pages now let's go ahead and npm run
1218.36 -> start which will start our app in
1220.52 -> production
1221.78 -> and uh we'll clear our API server again
1225.44 -> and let's refresh our app
1228.02 -> so uh we actually don't see our API
1231.86 -> server getting hit at all and this is
1234.38 -> because uh next was able to detect that
1237.14 -> these pages are static and by default it
1240.14 -> is actually caching the fetch requests
1242.48 -> that we're making right here so this is
1245 -> basically the equivalent of get static
1246.679 -> props now once we click on these links
1249.86 -> we're going to see the API request going
1252.14 -> out to the movies and this is because
1254.78 -> these pages are using a dynamic param
1258.14 -> and next at build time doesn't know
1260.6 -> which Pages it should pre-build so you
1263.299 -> might know from next 12 we have this API
1265.76 -> called get static paths which we can use
1267.98 -> to tell next to pre-build certain pages
1270.86 -> well in next 13 we can come to the page
1274.52 -> under our Dynamic segment
1276.86 -> and we can export a function called
1279.679 -> generate static params
1281.84 -> and here we're going to fetch all of our
1284.72 -> movies
1285.86 -> and we'll await that
1288.62 -> and then we can await response Json
1291.2 -> which is going to give us an array of
1293.84 -> movies
1294.86 -> and now all we have to do is return an
1297.5 -> array of objects with a key of ID to
1300.679 -> match the dynamic param so let's return
1302.6 -> movies.map
1304.94 -> and for each movie
1306.799 -> we will return an object with the ID
1311.96 -> now let's come back to our terminal
1315.98 -> go ahead and clear our API server and
1319.22 -> we'll run build again
1325.1 -> and look at that we see a lot more API
1327.08 -> endpoints being hit we see we have six
1329.84 -> static pages so now if we run npm run
1333.14 -> start
1334.34 -> and we go ahead and clear this
1338.78 -> we can refresh
1340.7 -> and look at that everything's instant
1344.419 -> we're not even depending on the API
1346.4 -> server at runtime so we can see that
1349.22 -> next capabilities for static rendering
1351.26 -> aren't going anywhere it's just that we
1353.78 -> no longer have to use any proprietary
1356.24 -> apis like get static props we still get
1359.179 -> to fetch data the exact same way in
1361.94 -> every page in every layout on the server
1364.52 -> using use and the power of suspense now
1367.7 -> of course these fetch calls can be
1369.14 -> configured to make requests at runtime
1371.6 -> that's a bit beyond the scope of this
1373.7 -> talk but uh I hope that got you excited
1376.22 -> for next 13 and all the cool stuff that
1378.86 -> comes along with nested layouts I can't
1381.14 -> wait to start building apps like this
1382.7 -> I've already had so much fun and it's
1384.919 -> really just the beginning so I hope you
1386.9 -> enjoyed that talk enjoy the rest of the
1389 -> conference and have a great day

Source: https://www.youtube.com/watch?v=6mQ3M1CUGnk