table of contents - topidesta.files.wordpress.com · first, the page takes longer to become visible...

102

Upload: others

Post on 18-Jun-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and
Page 2: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

TableofContentsIntroduction

IntroductiontoNext.js

ThemainfeaturesprovidedbyNext.js

Next.jsvsGatsbyvscreate-react-app

HowtoinstallNext.js?

ViewsourcetoconfirmSSRisworking

Theappbundles

What'sthaticononthebottomright?

InstalltheReactDeveloperTools

Otherdebuggingtechniquesyoucanuse

Addingasecondpagetothesite

Linkingthetwopages

Dynamiccontentwiththerouter

Prefetching

Usingtheroutertodetecttheactivelink

Usingnext/router

FeeddatatothecomponentsusinggetInitialProps

CSS

Populatingtheheadtagwithcustomtags

Addingawrappercomponent

APIRoutes

Runcodeonlyontheserversideorclientside

2

Page 3: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Deployingtheproductionversion

DeployingonNow

Analyzingtheappbundles

Lazyloadingmodules

Wheretogofromhere

3

Page 4: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Introduction

Welcome!IwrotethisbooktohelpyouquicklylearnNext.jsandgetfamiliarwithhowitworks.

TheidealreaderofthebookhaszeroknowledgeofNext.js,hasusedReactinthepast,andislookingforwarddivingmoreintotheReactecosystem,inparticularserver-siderendering.

IfindNext.jsanawesometooltocreateWebApplications,andattheendofthebookIhopeyou'llbeasexcitedaboutitasIam.

Thankyouforgettingthisebook.IhopeitwillhelpyoulearnNext.js!

Flavio

[email protected],onTwitter@flaviocopes.

Mywebsiteisflaviocopes.com.

Introduction

4

Page 5: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

IntroductiontoNext.jsWorkingonamodernJavaScriptapplicationpoweredbyReactisawesomeuntilyourealizethatthereareacoupleproblemsrelatedtorenderingallthecontentontheclient-side.

First,thepagetakeslongertobecomevisibletotheuser,becausebeforethecontentloads,alltheJavaScriptmustload,andyourapplicationneedstoruntodeterminewhattoshowonthepage.

Second,ifyouarebuildingapubliclyavailablewebsite,youhaveacontentSEOissue.SearchenginesaregettingbetteratrunningandindexingJavaScriptapps,butit'smuchbetterifwecansendthemcontentinsteadoflettingthemfigureitout.

Thesolutiontobothofthoseproblemsisserverrendering,alsocalledstaticpre-rendering.

Next.jsisoneReactframeworktodoallofthisinaverysimpleway,butit'snotlimitedtothis.It'sadvertisedbyitscreatorsasazero-configuration,single-commandtoolchainforReactapps.

ItprovidesacommonstructurethatallowsyoutoeasilybuildafrontendReactapplication,andtransparentlyhandlesserver-siderenderingforyou.

IntroductiontoNext.js

5

Page 6: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

ThemainfeaturesprovidedbyNext.jsHereisanon-exhaustivelistofthemainNext.jsfeatures:

HotCodeReloading

Next.jsreloadsthepagewhenitdetectsanychangesavedtodisk.

AutomaticRouting

AnyURLismappedtothefilesystem,tofilesputinthe pagesfolder,andyoudon'tneedanyconfiguration(youhavecustomizationoptionsofcourse).

SingleFileComponents

Using styled-jsx,completelyintegratedasbuiltbythesameteam,it'strivialtoaddstylesscopedtothecomponent.

ServerRendering

YoucanrenderReactcomponentsontheserverside,beforesendingtheHTMLtotheclient.

EcosystemCompatibility

Next.jsplayswellwiththerestoftheJavaScript,Node,andReactecosystem.

ThemainfeaturesprovidedbyNext.js

6

Page 7: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

AutomaticCodeSplitting

PagesarerenderedwithjustthelibrariesandJavaScriptthattheyneed,nomore.InsteadofgeneratingonesingleJavaScriptfilecontainingalltheappcode,theappisbrokenupautomaticallybyNext.jsinseveraldifferentresources.

LoadingapageonlyloadstheJavaScriptnecessaryforthatparticularpage.

Next.jsdoesthatbyanalyzingtheresourcesimported.

IfonlyoneofyourpagesimportstheAxioslibrary,forexample,thatspecificpagewillincludethelibraryinitsbundle.

Thisensuresyourfirstpageloadisasfastasitcanbe,andonlyfuturepageloads(iftheywilleverbetriggered)willsendtheJavaScriptneededtotheclient.

Thereisonenotableexception.FrequentlyusedimportsaremovedintothemainJavaScriptbundleiftheyareusedinatleasthalfofthesitepages.

Prefetching

The Linkcomponent,usedtolinktogetherdifferentpages,supportsa prefetchpropwhichautomaticallyprefetchespageresources(includingcodemissingduetocodesplitting)inthebackground.

DynamicComponents

YoucanimportJavaScriptmodulesandReactComponentsdynamically.

ThemainfeaturesprovidedbyNext.js

7

Page 8: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

StaticExports

Usingthe nextexportcommand,Next.jsallowsyoutoexportafullystaticsitefromyourapp.

TypeScriptSupport

Next.jsiswritteninTypeScriptandassuchcomeswithanexcellentTypeScriptsupport.

ThemainfeaturesprovidedbyNext.js

8

Page 9: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Next.jsvsGatsbyvscreate-react-appNext.js,Gatsby,and create-react-appareamazingtoolswecanusetopowerourapplications.

Let'sfirstsaywhattheyhaveincommon.TheyallhaveReactunderthehood,poweringtheentiredevelopmentexperience.Theyalsoabstractwebpackandallthoselowlevelthingsthatweusedtoconfiguremanuallyinthegoodolddays.

create-react-appdoesnothelpyougenerateaserver-side-renderedappeasily.Anythingthatcomeswithit(SEO,speed...)isonlyprovidedbytoolslikeNext.jsandGatsby.

WhenisNext.jsbetterthanGatsby?

Theycanbothhelpwithserver-siderendering,butin2differentways.

TheendresultusingGatsbyisastaticsitegenerator,withoutaserver.Youbuildthesite,andthenyoudeploytheresultofthebuildprocessstaticallyonNetlifyoranotherstatichostingsite.

Next.jsprovidesabackendthatcanserversiderenderaresponsetorequest,allowingyoutocreateadynamicwebsite,whichmeansyouwilldeployitonaplatformthatcanrunNode.js.

Next.jscangenerateastaticsitetoo,butIwouldnotsayit'sitsmainusecase.

Next.jsvsGatsbyvscreate-react-app

9

Page 10: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Ifmygoalwastobuildastaticsite,I'dhaveahardtimechoosingandperhapsGatsbyhasabetterecosystemofplugins,includingmanyforblogginginparticular.

GatsbyisalsoheavilybasedonGraphQL,somethingyoumightreallylikeordislikedependingonyouropinionsandneeds.

Next.jsvsGatsbyvscreate-react-app

10

Page 11: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

HowtoinstallNext.js?ToinstallNext.js,youneedtohaveNode.jsinstalled.

MakesurethatyouhavethelatestversionofNode.Checkwithrunning node-vinyourterminal,andcompareittothelatestLTSversionlistedonhttps://nodejs.org/.

AfteryouinstallNode.js,youwillhavethe npmcommandavailableintoyourcommandline.

Ifyouhaveanytroubleatthisstage,IrecommendthefollowingtutorialsIwroteforyou:

HowtoinstallNode.jsHowtoupdateNode.jsAnintroductiontothenpmpackagemanagerUnixShellsTutorialHowtousethemacOSterminalTheBashShell

NowthatyouhaveNode,updatedtothelatestversion,and npm,we'reset!

Wecanchoose2routesnow:using create-next-apportheclassicapproachwhichinvolvesinstallingandsettingupaNextappmanually.

Usingcreate-next-app

Ifyou'refamiliarwith create-react-app, create-next-appisthesamething-exceptitcreatesaNextappinsteadofaReactapp,asthenameimplies.

HowtoinstallNext.js?

11

Page 12: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

IassumeyouhavealreadyinstalledNode.js,which,fromversion5.2(2+yearsagoatthetimeofwriting),comeswiththe npxcommandbundled.ThishandytoolletsusdownloadandexecuteaJavaScriptcommand,andwe'lluseitlikethis:

npxcreate-next-app

Thecommandaskstheapplicationname(andcreatesanewfolderforyouwiththatname),thendownloadsallthepackagesitneeds( react, react-dom, next),setsthe package.jsonto:

andyoucanimmediatelyrunthesampleappbyrunning npmrundev:

HowtoinstallNext.js?

12

Page 13: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Andhere'stheresultonhttp://localhost:3000:

HowtoinstallNext.js?

13

Page 14: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

ThisistherecommendedwaytostartaNext.jsapplication,asitgivesyoustructureandsamplecodetoplaywith.There'smorethanjustthatdefaultsampleapplication;youcanuseanyoftheexamplesstoredathttps://github.com/zeit/next.js/tree/canary/examplesusingthe --exampleoption.Forexampletry:

npxcreate-next-app--exampleblog-starter

Whichgivesyouanimmediatelyusablebloginstancewithsyntaxhighlightingtoo:

HowtoinstallNext.js?

14

Page 15: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

ManuallycreateaNext.jsapp

Youcanavoid create-next-appifyoufeellikecreatingaNextappfromscratch.Here'show:createanemptyfolderanywhereyoulike,forexampleinyourhomefolder,andgointoit:

mkdirnextjs

cdnextjs

andcreateyourfirstNextprojectdirectory:

mkdirfirstproject

cdfirstproject

HowtoinstallNext.js?

15

Page 16: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Nowusethe npmcommandtoinitializeitasaNodeproject:

npminit-y

The -yoptiontells npmtousethedefaultsettingsforaproject,populatingasample package.jsonfile.

NowinstallNextandReact:

npminstallnextreactreact-dom

Yourprojectfoldershouldnowhave2files:

package.json(seemytutorialonit)package-lock.json(seemytutorialonpackage-lock)

andthe node_modulesfolder.

HowtoinstallNext.js?

16

Page 17: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Opentheprojectfolderusingyourfavoriteeditor.MyfavoriteeditorisVSCode.Ifyouhavethatinstalled,youcanrun code.inyourterminaltoopenthecurrentfolderintheeditor(ifthecommanddoesnotworkforyou,seethis)

Open package.json,whichnowhasthiscontent:

{

"name":"firstproject",

"version":"1.0.0",

"description":"",

"main":"index.js",

"scripts":{

"test":"echo\"Error:notestspecified\"&&exit1"

},

"keywords":[],

"author":"",

"license":"ISC",

"dependencies":{

"next":"^9.1.2",

"react":"^16.11.0",

"react-dom":"^16.11.0"

}

}

andreplacethe scriptssectionwith:

"scripts":{

"dev":"next",

"build":"nextbuild",

"start":"nextstart"

}

toaddtheNext.jsbuildcommands,whichwe'regoingtousesoon.

HowtoinstallNext.js?

17

Page 18: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Tip:use "dev":"next-p3001",tochangetheportandrun,inthisexample,onport3001.

Nowcreatea pagesfolder,andaddan index.jsfile.

Inthisfile,let'screateourfirstReactcomponent.

We'regoingtouseitasthedefaultexport:

constIndex=()=>(

<div>

<h1>Homepage</h1>

</div>

)

exportdefaultIndex

Nowusingtheterminal,run npmrundevtostarttheNextdevelopmentserver.

HowtoinstallNext.js?

18

Page 19: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Thiswillmaketheappavailableonport3000,onlocalhost.

Openhttp://localhost:3000inyourbrowsertoseeit.

HowtoinstallNext.js?

19

Page 20: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

HowtoinstallNext.js?

20

Page 21: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

ViewsourcetoconfirmSSRisworkingLet'snowchecktheapplicationisworkingasweexpectittowork.It'saNext.jsapp,soitshouldbeserversiderendered.

It'soneofthemainsellingpointsofNext.js:ifwecreateasiteusingNext.js,thesitepagesarerenderedontheserver,whichdeliversHTMLtothebrowser.

Thishas3majorbenefits:

TheclientdoesnotneedtoinstantiateReacttorender,whichmakesthesitefastertoyourusers.Searchengineswillindexthepageswithoutneedingtoruntheclient-sideJavaScript.SomethingGooglestarteddoing,butopenlyadmittedtobeaslowerprocess(andyoushouldhelpGoogleasmuchaspossible,ifyouwanttorankwell).Youcanhavesocialmediametatags,usefultoaddpreviewimages,customizetitleanddescriptionforanyofyourpagessharedonFacebook,Twitterandsoon.

Let'sviewthesourceoftheapp.UsingChromeyoucanright-clickanywhereinthepage,andpressViewPageSource.

ViewsourcetoconfirmSSRisworking

21

Page 22: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Ifyouviewthesourceofthepage,you'llseethe <div><h1>Homepage</h1></div>snippetintheHTML body,alongwithabunchofJavaScriptfiles-theappbundles.

Wedon'tneedtosetupanything,SSR(server-siderendering)isalreadyworkingforus.

TheReactappwillbelaunchedontheclient,andwillbetheonepoweringinteractionslikeclickingalink,usingclient-siderendering.Butreloadingapagewillre-loaditfromtheserver.AndusingNext.jsthereshouldbenodifferenceintheresultinsidethebrowser-aserver-renderedpageshouldlookexactlylikeaclient-renderedpage.

ViewsourcetoconfirmSSRisworking

22

Page 23: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

TheappbundlesWhenweviewedthepagesource,wesawabunchofJavaScriptfilesbeingloaded:

Let'sstartbyputtingthecodeinanHTMLformattertogetitformattedbetter,sowehumanscangetabetterchanceatunderstandingit:

<!DOCTYPEhtml>

<html>

<head>

<metacharSet="utf-8"/>

<metaname="viewport"content="width=device-width,minimum-sca

le=1,initial-scale=1"/>

<metaname="next-head-count"content="2"/>

Theappbundles

23

Page 24: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

<linkrel="preload"href="/_next/static/development/pages/ind

ex.js?ts=1572863116051"as="script"/>

<linkrel="preload"href="/_next/static/development/pages/_ap

p.js?ts=1572863116051"as="script"/>

<linkrel="preload"href="/_next/static/runtime/webpack.js?ts

=1572863116051"as="script"/>

<linkrel="preload"href="/_next/static/runtime/main.js?ts=15

72863116051"as="script"/>

</head>

<body>

<divid="__next">

<div>

<h1>Homepage</h1></div>

</div>

<scriptsrc="/_next/static/development/dll/dll_01ec57fc9b90d4

3b98a8.js?ts=1572863116051"></script>

<scriptid="__NEXT_DATA__"type="application/json">{"dataMana

ger":"[]","props":{"pageProps":{}},"page":"/","query":{},"buildId"

:"development","nextExport":true,"autoExport":true}</script>

<scriptasync=""data-next-page="/"src="/_next/static/develo

pment/pages/index.js?ts=1572863116051"></script>

<scriptasync=""data-next-page="/_app"src="/_next/static/de

velopment/pages/_app.js?ts=1572863116051"></script>

<scriptsrc="/_next/static/runtime/webpack.js?ts=157286311605

1"async=""></script>

<scriptsrc="/_next/static/runtime/main.js?ts=1572863116051"

async=""></script>

</body>

</html>

Wehave4JavaScriptfilesbeingdeclaredtobepreloadedinthehead,using rel="preload"as="script":

/_next/static/development/pages/index.js(96LOC)/_next/static/development/pages/_app.js(5900LOC)

Theappbundles

24

Page 25: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

/_next/static/runtime/webpack.js(939LOC)/_next/static/runtime/main.js(12kLOC)

Thistellsthebrowsertostartloadingthosefilesassoonaspossible,beforethenormalrenderingflowstarts.Withoutthose,scriptswouldbeloadedwithanadditionaldelay,andthisimprovesthepageloadingperformance.

Thenthose4filesareloadedattheendofthe body,alongwith/_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js(31kLOC),andaJSONsnippetthatsetssomedefaultsforthepagedata:

<scriptid="__NEXT_DATA__"type="application/json">

{

"dataManager":"[]",

"props":{

"pageProps":{}

},

"page":"/",

"query":{},

"buildId":"development",

"nextExport":true,

"autoExport":true

}

</script>

The4bundlefilesloadedarealreadyimplementingonefeaturecalledcodesplitting.The index.jsfileprovidesthecodeneededfortheindexcomponent,whichservesthe /route,andifwehadmorepageswe'dhavemorebundlesforeachpage,whichwillthenonlybeloadedifneeded-toprovideamoreperformantloadtimeforthepage.

Theappbundles

25

Page 26: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Theappbundles

26

Page 27: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

What'sthaticononthebottomright?Didyouseethatlittleiconatthebottomrightofthepage,whichlookslikealightning?

Ifyouhoverit,it'sgoingtosay"PrerenderedPage":

What'sthaticononthebottomright?

27

Page 28: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Thisicon,whichisonlyvisibleindevelopmentmodeofcourse,tellsyouthepagequalifiesforautomaticstaticoptimization,whichbasicallymeansthatitdoesnotdependondatathatneedstobefetchedatinvokationtime,anditcanbeprerenderedandbuiltasastaticHTMLfileatbuildtime(whenwerun npmrunbuild).

Nextcandeterminethisbytheabsenceofthe getInitialProps()methodattachedtothepagecomponent.

Whenthisisthecase,ourpagecanbeevenfasterbecauseitwillbeservedstaticallyasanHTMLfileratherthangoingthroughtheNode.jsserverthatgeneratestheHTMLoutput.

Anotherusefuliconthatmightappearnexttoit,orinsteadofitonnon-prerenderedpages,isalittleanimatedtriangle:

What'sthaticononthebottomright?

28

Page 29: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Thisisacompilationindicator,andappearswhenyousaveapageandNext.jsiscompilingtheapplicationbeforehotcodereloadingkicksintoreloadthecodeintheapplicationautomatically.

It'sareallynicewaytoimmediatelydetermineiftheapphasalreadybeencompiledandyoucantestapartofityou'reworkingon.

What'sthaticononthebottomright?

29

Page 30: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

InstalltheReactDeveloperToolsNext.jsisbasedonReact,sooneveryusefultoolweabsolutelyneedtoinstall(ifyouhaven'talready)istheReactDeveloperTools.

AvailableforbothChromeandFirefox,theReactDeveloperToolsareanessentialinstrumentyoucanusetoinspectaReactapplication.

Now,theReactDeveloperToolsarenotspecifictoNext.jsbutIwanttointroducethembecauseyoumightnotbe100%familiarwithallthetoolsReactprovides.It'sbesttogoalittleintodebuggingtoolingthanassumingyoualreadyknowthem.

TheyprovideaninspectorthatrevealstheReactcomponentstreethatbuildsyourpage,andforeachcomponentyoucangoandchecktheprops,thestate,hooks,andlotsmore.

OnceyouhaveinstalledtheReactDeveloperTools,youcanopentheregularbrowserdevtools(inChrome,it'sright-clickinthepage,thenclick Inspect)andyou'llfind2newpanels:ComponentsandProfiler.

InstalltheReactDeveloperTools

30

Page 31: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Ifyoumovethemouseoverthecomponents,you'llseethatinthepage,thebrowserwillselectthepartsthatarerenderedbythatcomponent.

Ifyouselectanycomponentinthetree,therightpanelwillshowyouareferencetotheparentcomponent,andthepropspassedtoit:

InstalltheReactDeveloperTools

31

Page 32: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Youcaneasilynavigatebyclickingaroundthecomponentnames.

YoucanclicktheeyeiconintheDeveloperToolstoolbartoinspecttheDOMelement,andalsoifyouusethefirsticon,theonewiththemouseicon(whichconvenientlysitsunderthesimilarregularDevToolsicon),youcanhoveranelementinthebrowserUItodirectlyselecttheReactcomponentthatrendersit.

Youcanusethe bugicontologacomponentdatatotheconsole.

InstalltheReactDeveloperTools

32

Page 33: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Thisisprettyawesomebecauseonceyouhavethedataprintedthere,youcanright-clickanyelementandpress"Storeasaglobalvariable".ForexamplehereIdiditwiththe urlprop,andIwasabletoinspectitintheconsoleusingthetemporaryvariableassignedtoit, temp1:

InstalltheReactDeveloperTools

33

Page 34: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

UsingSourceMaps,whichareloadedbyNext.jsautomaticallyindevelopmentmode,fromtheComponentspanelwecanclickthe <>codeandtheDevToolswillswitchtotheSourcepanel,showingusthecomponentsourcecode:

InstalltheReactDeveloperTools

34

Page 35: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

TheProfilertabisevenmoreawesome,ifpossible.Itallowsustorecordaninteractionintheapp,andseewhathappens.Icannotshowanexampleyet,becauseitneedsatleast2componentstocreateaninteraction,andwehavejustonenow.I'lltalkaboutthislater.

InstalltheReactDeveloperTools

35

Page 36: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

IshowedallscreenshotsusingChrome,buttheReactDeveloperToolsworksinthesamewayinFirefox:

InstalltheReactDeveloperTools

36

Page 37: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

InstalltheReactDeveloperTools

37

Page 38: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

OtherdebuggingtechniquesyoucanuseInadditiontotheReactDeveloperTools,whichareessentialtobuildingaNext.jsapplication,Iwanttoemphasize2waystodebugNext.jsapps.

Thefirstisobviously console.log()andalltheotherConsoleAPItools.ThewayNextappsworkwillmakealogstatementworkinthebrowserconsoleORintheterminalwhereyoustartedNextusing npmrundev.

Inparticular,ifthepageloadsfromtheserver,whenyoupointtheURLtoit,oryouhittherefreshbutton/cmd/ctrl-R,anyconsolelogginghappensintheterminal.

Subsequentpagetransitionsthathappenbyclickingthemousewillmakeallconsolelogginghappeninsidethebrowser.

Justrememberifyouaresurprisedbymissinglogging.

Anothertoolthatisessentialisthe debuggerstatement.Addingthisstatementtoacomponentwillpausethebrowserrenderingthepage:

Otherdebuggingtechniquesyoucanuse

38

Page 39: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Reallyawesomebecausenowyoucanusethebrowserdebuggertoinspectvaluesandrunyourapponelineatatime.

Otherdebuggingtechniquesyoucanuse

39

Page 40: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

YoucanalsousetheVSCodedebuggertodebugserver-sidecode.Imentionthistechniqueandthistutorialtosetthisup.

Otherdebuggingtechniquesyoucanuse

40

Page 41: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

AddingasecondpagetothesiteNowthatwehaveagoodgraspofthetoolswecanusetohelpusdevelopNext.jsapps,let'scontinuefromwhereweleftourfirstapp:

Iwanttoaddasecondpagetothiswebsite,ablog.It'sgoingtobeservedinto /blog,andforthetimebeingitwilljustcontainasimplestaticpage,justlikeourfirst index.jscomponent:

Addingasecondpagetothesite

41

Page 42: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Aftersavingthenewfile,the npmrundevprocessalreadyrunningisalreadycapableofrenderingthepage,withouttheneedtorestartit.

WhenwehittheURLhttp://localhost:3000/blogwehavethenewpage:

Addingasecondpagetothesite

42

Page 43: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

andhere'swhattheterminaltoldus:

Addingasecondpagetothesite

43

Page 44: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

NowthefactthattheURLis /blogdependsonjustthefilename,anditspositionunderthe pagesfolder.

Youcancreatea pages/hey/hopage,andthatpagewillshowupontheURLhttp://localhost:3000/hey/ho.

Whatdoesnotmatter,fortheURLpurposes,isthecomponentnameinsidethefile.

Trygoingandviewingthesourceofthepage,whenloadedfromtheserveritwilllist /_next/static/development/pages/blog.jsasoneofthebundlesloaded,andnot/_next/static/development/pages/index.jslikeinthehomepage.Thisisbecausethankstoautomaticcodesplittingwedon'tneedthebundlethatservesthehomepage.Justthebundlethatservestheblogpage.

Wecanalsojustexportananonymousfunctionfrom blog.js:

exportdefault()=>(

<div>

<h1>Blog</h1>

Addingasecondpagetothesite

44

Page 45: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

</div>

)

orifyoupreferthenon-arrowfunctionsyntax:

exportdefaultfunction(){

return(

<div>

<h1>Blog</h1>

</div>

)

}

Addingasecondpagetothesite

45

Page 46: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

LinkingthetwopagesNowthatwehave2pages,definedby index.jsand blog.js,wecanintroducelinks.

NormalHTMLlinkswithinpagesaredoneusingthe atag:

<ahref="/blog">Blog</a>

Wecan'tdodothatinNext.js.

Why?Wetechnicallycan,ofcourse,becausethisistheWebandontheWebthingsneverbreak(that'swhywecanstillusethe <marquee>tag.ButoneofthemainbenefitsofusingNextisthatonceapageisloaded,transitionstootherpageareveryfastthankstoclient-siderendering.

Ifyouuseaplain alink:

constIndex=()=>(

<div>

<h1>Homepage</h1>

<ahref='/blog'>Blog</a>

</div>

)

exportdefaultIndex

NowopentheDevTools,andtheNetworkpanelinparticular.Thefirsttimeweload http://localhost:3000/wegetallthepagebundlesloaded:

Linkingthetwopages

46

Page 47: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Nowifyouclickthe"Preservelog"button(toavoidclearingtheNetworkpanel),andclickthe"Blog"link,thisiswhathappens:

Linkingthetwopages

47

Page 48: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

WegotallthatJavaScriptfromtheserver,again!But..wedon'tneedallthatJavaScriptifwealreadygotit.We'djustneedthe blog.jspagebundle,theonlyonethat'snewtothepage.

Tofixthisproblem,weuseacomponentprovidedbyNext,calledLink.

Weimportit:

importLinkfrom'next/link'

andthenweuseittowrapourlink,likethis:

importLinkfrom'next/link'

Linkingthetwopages

48

Page 49: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

constIndex=()=>(

<div>

<h1>Homepage</h1>

<Linkhref='/blog'>

<a>Blog</a>

</Link>

</div>

)

exportdefaultIndex

Nowifyouretrythethingwedidpreviously,you'llbeabletoseethatonlythe blog.jsbundleisloadedwhenwemovetotheblogpage:

Linkingthetwopages

49

Page 50: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

andthepageloadedsofasterthanbefore,thebrowserusualspinneronthetabdidn'tevenappear.YettheURLchanged,asyoucansee.ThisisworkingseamlesslywiththebrowserHistoryAPI.

Thisisclient-siderenderinginaction.

Whatifyounowpressthebackbutton?Nothingisbeingloaded,becausethebrowserstillhastheold index.jsbundleinplace,readytoloadthe /indexroute.It'sallautomatic!

Linkingthetwopages

50

Page 51: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

DynamiccontentwiththerouterInthepreviouschapterwesawhowtolinkthehometotheblogpage.

AblogisagreatusecaseforNext.js,onewe'llcontinuetoexploreinthischapterbyaddingblogposts.

BlogpostshaveadynamicURL.Forexampleaposttitled"HelloWorld"mighthavetheURL /blog/hello-world.Aposttitled"Mysecondpost"mighthavetheURL /blog/my-second-post.

Thiscontentisdynamic,andmightbetakenfromadatabase,markdownfilesormore.

Next.jscanservedynamiccontentbasedonadynamicURL.

WecreateadynamicURLbycreatingadynamicpagewiththe []syntax.

How?Weadda pages/blog/[id].jsfile.ThisfilewillhandleallthedynamicURLsunderthe /blog/route,liketheoneswementionedabove: /blog/hello-world, /blog/my-second-postandmore.

Inthefilename, [id]insidethesquarebracketsmeansthatanythingthat'sdynamicwillbeputinsidethe idparameterofthequerypropertyoftherouter.

Ok,that'sabittoomanythingsatonce.

What'stherouter?

TherouterisalibraryprovidedbyNext.js.

Weimportitfrom next/router:

Dynamiccontentwiththerouter

51

Page 52: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

import{useRouter}from'next/router'

andoncewehave useRouter,weinstantiatetherouterobjectusing:

constrouter=useRouter()

Oncewehavethisrouterobject,wecanextractinformationfromit.

InparticularwecangetthedynamicpartoftheURLinthe [id].jsfilebyaccessing router.query.id.

ThedynamicpartcanalsojustbeaportionoftheURL,like post-[id].js.

Solet'sgoonandapplyallthosethingsinpractice.

Createthefile pages/blog/[id].js:

import{useRouter}from'next/router'

exportdefault()=>{

constrouter=useRouter()

return(

<>

<h1>Blogpost</h1>

<p>Postid:{router.query.id}</p>

</>

)

}

Nowifyougotothe http://localhost:3000/blog/testrouter,youshouldseethis:

Dynamiccontentwiththerouter

52

Page 53: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Wecanusethis idparametertogatherthepostfromalistofposts.Fromadatabase,forexample.Tokeepthingssimplewe'lladdaposts.jsonfileintheprojectrootfolder:

{

"test":{

"title":"testpost",

"content":"Heysomepostcontent"

},

"second":{

"title":"secondpost",

"content":"Heythisisthesecondpostcontent"

}

}

Nowwecanimportitandlookupthepostfromthe idkey:

Dynamiccontentwiththerouter

53

Page 54: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

import{useRouter}from'next/router'

importpostsfrom'../../posts.json'

exportdefault()=>{

constrouter=useRouter()

constpost=posts[router.query.id]

return(

<>

<h1>{post.title}</h1>

<p>{post.content}</p>

</>

)

}

Reloadingthepageshouldshowusthisresult:

Dynamiccontentwiththerouter

54

Page 55: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Butit'snot!Instead,wegetanerrorintheconsole,andanerrorinthebrowser,too:

Why?Because..duringrendering,whenthecomponentisinitialized,thedataisnotthereyet.We'llseehowtoprovidethedatatothecomponentwithgetInitialPropsinthenextlesson.

Fornow,addalittle if(!post)return<p></p>checkbeforereturningtheJSX:

import{useRouter}from'next/router'

importpostsfrom'../../posts.json'

exportdefault()=>{

constrouter=useRouter()

constpost=posts[router.query.id]

if(!post)return<p></p>

Dynamiccontentwiththerouter

55

Page 56: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

return(

<>

<h1>{post.title}</h1>

<p>{post.content}</p>

</>

)

}

Nowthingsshouldwork.Initiallythecomponentisrenderedwithoutthedynamic router.query.idinformation.Afterrendering,Next.jstriggersanupdatewiththequeryvalueandthepagedisplaysthecorrectinformation.

Andifyouviewsource,thereisthatempty <p>tagintheHTML:

Dynamiccontentwiththerouter

56

Page 57: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

We'llsoonfixthisissuethatfailstoimplementSSRandthisharmsbothloadingtimesforourusers,SEOandsocialsharingaswealreadydiscussed.

Wecancompletetheblogexamplebylistingthosepostsinpages/blog.js:

importpostsfrom'../posts.json'

constBlog=()=>(

<div>

<h1>Blog</h1>

<ul>

{Object.entries(posts).map((value,index)=>{

return<likey={index}>{value[1].title}</li>

})}

</ul>

</div>

)

exportdefaultBlog

Andwecanlinkthemtotheindividualpostpages,byimporting Linkfrom next/linkandusingitinsidethepostsloop:

importLinkfrom'next/link'

importpostsfrom'../posts.json'

constBlog=()=>(

<div>

<h1>Blog</h1>

<ul>

{Object.entries(posts).map((value,index)=>{

return(

<likey={index}>

Dynamiccontentwiththerouter

57

Page 58: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

<Linkhref='/blog/[id]'as={'/blog/'+value[0]}>

<a>{value[1].title}</a>

</Link>

</li>

)

})}

</ul>

</div>

)

exportdefaultBlog

Dynamiccontentwiththerouter

58

Page 59: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

PrefetchingImentionedpreviouslyhowthe LinkNext.jscomponentcanbeusedtocreatelinksbetween2pages,andwhenyouuseit,Next.jstransparentlyhandlesfrontendroutingforus,sowhenauserclicksalink,frontendtakescareofshowingthenewpagewithouttriggeringanewclient/serverrequestandresponsecycle,asitnormallyhappenswithwebpages.

There'sanotherthingthatNext.jsdoesforyouwhenyouuse Link.

Assoonasanelementwrappedwithin <Link>appearsintheviewport(whichmeansit'svisibletothewebsiteuser),Next.jsprefetchestheURLitpointsto,aslongasit'salocallink(onyourwebsite),makingtheapplicationsuperfasttotheviewer.

Thisbehaviorisonlybeingtriggeredinproductionmode(we'lltalkaboutthisin-depthlater),whichmeansyouhavetostoptheapplicationifyouarerunningitwith npmrundev,compileyourproductionbundlewith npmrunbuildandrunitwith npmrunstartinstead.

UsingtheNetworkinspectorintheDevToolsyou'llnoticethatanylinksabovethefold,atpageload,starttheprefetchingassoonastheloadeventhasbeenfiredonyourpage(triggeredwhenthepageisfullyloaded,andhappensafterthe DOMContentLoadedevent).

Anyother Linktagnotintheviewportwillbeprefetchedwhentheuserscrollsandit

Prefetchingisautomaticonhighspeedconnections(Wifiand3g+connections,unlessthebrowsersendsthe Save-DataHTTPHeader.

Prefetching

59

Page 60: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Youcanoptoutfromprefetchingindividual Linkinstancesbysettingthe prefetchpropto false:

<Linkhref="/a-link"prefetch={false}>

<a>Alink</a>

</Link>

Prefetching

60

Page 61: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

UsingtheroutertodetecttheactivelinkOneveryimportantfeaturewhenworkingwithlinksisdeterminingwhatisthecurrentURL,andinparticularassigningaclasstotheactivelink,sowecanstyleitdifferentlyfromtheotherones.

Thisisespeciallyusefulinyoursiteheader,forexample.

TheNext.jsdefault Linkcomponentofferedin next/linkdoesnotdothisautomaticallyforus.

WecancreateaLinkcomponentourselves,andwestoreitinafileLink.jsintheComponentsfolder,andimportthatinsteadofthedefault next/link.

Inthiscomponent,we'llfirstimportReactfrom react,Linkfromnext/linkandthe useRouterhookfrom next/router.

Insidethecomponentwedetermineifthecurrentpathnamematchesthe hrefpropofthecomponent,andifsoweappendthe selectedclasstothechildren.

Wefinallyreturnthischildrenwiththeupdatedclass,usingReact.cloneElement():

importReactfrom'react'

importLinkfrom'next/link'

import{useRouter}from'next/router'

exportdefault({href,children})=>{

constrouter=useRouter()

Usingtheroutertodetecttheactivelink

61

Page 62: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

letclassName=children.props.className||''

if(router.pathname===href){

className=`${className}selected`

}

return<Linkhref={href}>{React.cloneElement(children,{classN

ame})}</Link>

}

Usingtheroutertodetecttheactivelink

62

Page 63: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Usingnext/routerWealreadysawhowtousetheLinkcomponenttodeclarativelyhandleroutinginNext.jsapps.

It'sreallyhandytomanageroutinginJSX,butsometimesyouneedtotriggeraroutingchangeprogrammatically.

Inthiscase,youcanaccesstheNext.jsRouterdirectly,providedinthenext/routerpackage,andcallits push()method.

Here'sanexampleofaccessingtherouter:

import{useRouter}from'next/router'

exportdefault()=>{

constrouter=useRouter()

//...

}

Oncewegettherouterobjectbyinvoking useRouter(),wecanuseitsmethods.

Thisistheclientsiderouter,somethodsshouldonlybeusedinfrontendfacingcode.Theeasiestwaytoensurethisistowrapcallsinthe useEffect()Reacthook,orinsidecomponentDidMount()inReactstatefulcomponents.

Theonesyou'lllikelyusethemostare push()and prefetch().

push()allowsustoprogrammaticallytriggeraURLchange,inthefrontend:

Usingnext/router

63

Page 64: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

router.push('/login')

prefetch()allowsustoprogrammaticallyprefetchaURL,usefulwhenwedon'thavea Linktagwhichautomaticallyhandlesprefetchingforus:

router.prefetch('/login')

Fullexample:

import{useRouter}from'next/router'

exportdefault()=>{

constrouter=useRouter()

useEffect(()=>{

router.prefetch('/login')

})

}

Youcanalsousetheroutertolistenforroutechangeevents.

Usingnext/router

64

Page 65: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

FeeddatatothecomponentsusinggetInitialPropsInthepreviouschapterwehadanissuewithdynamicallygeneratingthepostpage,becausethecomponentrequiredsomedataupfront,andwhenwetriedtogetthedatafromtheJSONfile:

import{useRouter}from'next/router'

importpostsfrom'../../posts.json'

exportdefault()=>{

constrouter=useRouter()

constpost=posts[router.query.id]

return(

<>

<h1>{post.title}</h1>

<p>{post.content}</p>

</>

)

}

wegotthiserror:

FeeddatatothecomponentsusinggetInitialProps

65

Page 66: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Howdowesolvethis?AndhowdowemakeSSRworkfordynamicroutes?

Wemustprovidethecomponentwithprops,usingaspecialfunctioncalled getInitialProps()whichisattachedtothecomponent.

Todoso,firstwenamethecomponent:

constPost=()=>{

//...

}

exportdefaultPost

thenweaddthefunctiontoit:

constPost=()=>{

FeeddatatothecomponentsusinggetInitialProps

66

Page 67: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

//...

}

Post.getInitialProps=()=>{

//...

}

exportdefaultPost

Thisfunctiongetsanobjectasitsargument,whichcontainsseveralproperties.Inparticular,thethingweareinterestedintonowisthatwegetthe queryobject,theoneweusedpreviouslytogetthepostid.

Sowecangetitusingtheobjectdestructuringsyntax:

Post.getInitialProps=({query})=>{

//...

}

Nowwecanreturnthepostfromthisfunction:

Post.getInitialProps=({query})=>{

return{

post:posts[query.id]

}

}

Andwecanalsoremovetheimportof useRouter,andwegetthepostfromthe propspropertypassedtothe Postcomponent:

importpostsfrom'../../posts.json'

constPost=props=>{

return(

<div>

FeeddatatothecomponentsusinggetInitialProps

67

Page 68: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

<h1>{props.post.title}</h1>

<p>{props.post.content}</p>

</div>

)

}

Post.getInitialProps=({query})=>{

return{

post:posts[query.id]

}

}

exportdefaultPost

Nowtherewillbenoerror,andSSRwillbeworkingasexpected,asyoucanseecheckingviewsource:

FeeddatatothecomponentsusinggetInitialProps

68

Page 69: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

The getInitialPropsfunctionwillbeexecutedontheserverside,butalsoontheclientside,whenwenavigatetoanewpageusingtheLinkcomponentaswedid.

It'simportanttonotethat getInitialPropsgets,inthecontextobjectitreceives,inadditiontothe queryobjecttheseotherproperties:

pathname:the pathsectionofURLasPath-Stringoftheactualpath(includingthequery)showsinthebrowser

whichinthecaseofcalling http://localhost:3000/blog/testwillrespectivelyresultto:

/blog/[id]

/blog/test

Andinthecaseofserversiderendering,itwillalsoreceive:

req:theHTTPrequestobjectres:theHTTPresponseobjecterr:anerrorobject

reqand reswillbefamiliartoyouifyou'vedoneanyNode.jscoding.

FeeddatatothecomponentsusinggetInitialProps

69

Page 70: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

CSSHowdowestyleReactcomponentsinNext.js?

Wehavealotoffreedom,becausewecanusewhateverlibraryweprefer.

ButNext.jscomeswith styled-jsxbuilt-in,becausethat'salibrarybuiltbythesamepeopleworkingonNext.js.

Andit'saprettycoollibrarythatprovidesusscopedCSS,whichisgreatformaintainabilitybecausetheCSSisonlyaffectingthecomponentit'sappliedto.

IthinkthisisagreatapproachatwritingCSS,withouttheneedtoapplyadditionallibrariesorpreprocessorsthataddcomplexity.

ToaddCSStoaReactcomponentinNext.jsweinsertitinsideasnippetintheJSX,whichstartwith

<stylejsx>{`

andendswith

`}</style>

InsidethisweirdblockswewriteplainCSS,aswe'ddoina .cssfile:

<stylejsx>{`

h1{

font-size:3rem;

}

CSS

70

Page 71: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

`}</style>

YouwriteitinsidetheJSX,likethis:

constIndex=()=>(

<div>

<h1>Homepage</h1>

<stylejsx>{`

h1{

font-size:3rem;

}

`}</style>

</div>

)

exportdefaultIndex

Insidetheblockwecanuseinterpolationtodynamicallychangethevalues.Forexamplehereweassumea sizepropisbeingpassedbytheparentcomponent,andweuseitinthe styled-jsxblock:

constIndex=props=>(

<div>

<h1>Homepage</h1>

<stylejsx>{`

h1{

font-size:${props.size}rem;

}

`}</style>

</div>

)

CSS

71

Page 72: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

IfyouwanttoapplysomeCSSglobally,notscopedtoacomponent,youaddthe globalkeywordtothe styletag:

<stylejsxglobal>{`

body{

margin:0;

}

`}</style>

IfyouwanttoimportanexternalCSSfileinaNext.jscomponent,youhavetofirstinstall @zeit/next-css:

npminstall@zeit/next-css

andthencreateaconfigurationfileintherootoftheproject,callednext.config.js,withthiscontent:

constwithCSS=require('@zeit/next-css')

module.exports=withCSS()

AfterrestartingtheNextapp,youcannowimportCSSlikeyounormallydowithJavaScriptlibrariesorcomponents:

import'../style.css'

YoucanalsoimportaSASSfiledirectly,usingthe @zeit/next-sasslibraryinstead.

CSS

72

Page 73: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

PopulatingtheheadtagwithcustomtagsFromanyNext.jspagecomponent,youcanaddinformationtothepageheader.

Thisishandywhen:

youwanttocustomizethepagetitleyouwanttochangeametatag

Howcanyoudoso?

Insideeverycomponentyoucanimportthe Headcomponentfromnext/headandincludeitinyourcomponentJSXoutput:

importHeadfrom'next/head'

constHouse=props=>(

<div>

<Head>

<title>Thepagetitle</title>

</Head>

{/*therestoftheJSX*/}

</div>

)

exportdefaultHouse

YoucanaddanyHTMLtagyou'dliketoappearinthe <head>sectionofthepage.

Populatingtheheadtagwithcustomtags

73

Page 74: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Whenmountingthecomponent,Next.jswillmakesurethetagsinsideHeadareaddedtotheheadingofthepage.Samewhenunmountingthecomponent,Next.jswilltakecareofremovingthosetags.

Populatingtheheadtagwithcustomtags

74

Page 75: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

AddingawrappercomponentAllthepagesonyoursitelookmoreorlessthesame.There'sachromewindow,acommonbaselayer,andyoujustwanttochangewhat'sinside.

There'sanavbar,asidebar,andthentheactualcontent.

HowdoyoubuildsuchsysteminNext.js?

Thereare2ways.OneisusingaHigherOrderComponent,bycreatinga components/Layout.jscomponent:

exportdefaultPage=>{

return()=>(

<div>

<nav>

<ul>....</ul>

</hav>

<main>

<Page/>

</main>

</div>

)

}

Intherewecanimportseparatecomponentsforheadingand/orsidebar,andwecanalsoaddalltheCSSweneed.

Andyouuseitineverypagelikethis:

importwithLayoutfrom'../components/Layout.js'

constPage=()=><p>Here'sapage!</p>

Addingawrappercomponent

75

Page 76: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

exportdefaultwithLayout(Page)

ButIfoundthisworksonlyforsimplecases,whereyoudon'tneedtocall getInitialProps()onapage.

Why?

Because getInitialProps()getsonlycalledonthepagecomponent.ButifweexporttheHigherOrderComponentwithLayout()fromapage, Page.getInitialProps()isnotcalled.withLayout.getInitialProps()would.

Toavoidunnecessarilycomplicatingourcodebase,thealternativeapproachistouseprops:

exportdefaultprops=>(

<div>

<nav>

<ul>....</ul>

</hav>

<main>

{props.content}

</main>

</div>

)

andinourpagesnowweuseitlikethis:

importLayoutfrom'../components/Layout.js'

constPage=()=>(

<Layoutcontent={(

<p>Here'sapage!</p>

)}/>

Addingawrappercomponent

76

Page 77: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

)

Thisapproachletsususe getInitialProps()fromwithinourpagecomponent,withtheonlydownsideofhavingtowritethecomponentJSXinsidethe contentprop:

importLayoutfrom'../components/Layout.js'

constPage=()=>(

<Layoutcontent={(

<p>Here'sapage!</p>

)}/>

)

Page.getInitialProps=({query})=>{

//...

}

Addingawrappercomponent

77

Page 78: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

APIRoutesInadditiontocreatingpageroutes,whichmeanspagesareservedtothebrowserasWebpages,Next.jscancreateAPIroutes.

ThisisaveryinterestingfeaturebecauseitmeansthatNext.jscanbeusedtocreateafrontendfordatathatisstoredandretrievedbyNext.jsitself,transferringJSONviafetchrequests.

APIroutesliveunderthe /pages/api/folderandaremappedtothe/apiendpoint.

Thisfeatureisveryusefulwhencreatingapplications.

Inthoseroutes,wewriteNode.jscode(ratherthanReactcode).It'saparadigmshift,youmovefromthefrontendtothebackend,butveryseamlessly.

Sayyouhavea /pages/api/comments.jsfile,whosegoalistoreturnthecommentsofablogpostasJSON.

Sayyouhavealistofcommentsstoredina comments.jsonfile:

[

{

"comment":"First"

},

{

"comment":"Nicepost"

}

]

Here'sasamplecode,whichreturnstotheclientthelistofcomments:

APIRoutes

78

Page 79: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

importcommentsfrom'./comments.json'

exportdefault(req,res)=>{

res.status(200).json(comments)

}

Itwilllistenonthe /api/commentsURLforGETrequests,andyoucantrycallingitusingyourbrowser:

APIroutescanalsousedynamicroutinglikepages,usethe []syntaxtocreateadynamicAPIroute,like/pages/api/comments/[id].jswhichwillretrievethecommentsspecifictoapostid.

APIRoutes

79

Page 80: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Insidethe [id].jsyoucanretrievethe idvaluebylookingitupinsidethe req.queryobject:

importcommentsfrom'../comments.json'

exportdefault(req,res)=>{

res.status(200).json({post:req.query.id,comments})

}

Heresyoucanseetheabovecodeinaction:

Indynamicpages,you'dneedtoimport useRouterfrom next/router,thengettherouterobjectusing constrouter=useRouter(),andthenwe'dbeabletogetthe idvalueusing router.query.id.

Intheserver-sideit'salleasier,asthequeryisattachedtotherequestobject.

IfyoudoaPOSTrequest,allworksinthesameway-itallgoesthroughthatdefaultexport.

ToseparatePOSTfromGETandotherHTTPmethods(PUT,DELETE),lookupthe req.methodvalue:

APIRoutes

80

Page 81: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

exportdefault(req,res)=>{

switch(req.method){

case'GET':

//...

break

case'POST':

//...

break

default:

res.status(405).end()//MethodNotAllowed

break

}

}

Inadditionto req.queryand req.methodwealreadysaw,wehaveaccesstocookiesbyreferencing req.cookies,therequestbodyinreq.body.

Underthehoods,thisisallpoweredbyMicro,alibrarythatpowersasynchronousHTTPmicroservices,madebythesameteamthatbuiltNext.js.

YoucanmakeuseofanyMicromiddlewareinourAPIroutestoaddmorefunctionality.

APIRoutes

81

Page 82: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

RuncodeonlyontheserversideorclientsideInyourpagecomponents,youcanexecutecodeonlyintheserver-sideorontheclient-side,bycheckingthe windowproperty.

Thispropertyisonlyexistinginsidethebrowser,soyoucancheck

if(typeofwindow==='undefined'){

}

andaddtheserver-sidecodeinthatblock.

Similarly,youcanexecuteclient-sidecodeonlybychecking

if(typeofwindow!=='undefined'){

}

JSTip:Weusethe typeofoperatorherebecausewecan'tdetectavaluetobeundefinedinotherways.Wecan'tdo if(window===undefined)becausewe'dgeta"windowisnotdefined"runtimeerror

Next.js,asabuild-timeoptimization,alsoremovesthecodethatusesthosechecksfrombundles.Aclient-sidebundlewillnotincludethecontentwrappedintoa if(typeofwindow==='undefined'){}block.

Runcodeonlyontheserversideorclientside

82

Page 83: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Runcodeonlyontheserversideorclientside

83

Page 84: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

DeployingtheproductionversionDeployinganappisalwaysleftlastintutorials.

HereIwanttointroduceitearly,justbecauseit'ssoeasytodeployaNext.jsappthatwecandiveintoitnow,andthenmoveontoothermorecomplextopicslateron.

Rememberinthe"HowtoinstallNext.js"chapterItoldyoutoaddthose3linestothe package.json scriptsection:

"scripts":{

"dev":"next",

"build":"nextbuild",

"start":"nextstart"

}

Weused npmrundevuptonow,tocallthe nextcommandinstalledlocallyin node_modules/next/dist/bin/next.Thisstartedthedevelopmentserver,whichprovidedussourcemapsandhotcodereloading,twoveryusefulfeatureswhiledebugging.

Thesamecommandcanbeinvokedtobuildthewebsitepassingthebuildflag,byrunning npmrunbuild.Then,thesamecommandcanbeusedtostarttheproductionapppassingthe startflag,byrunning npmrunstart.

Those2commandsaretheoneswemustinvoketosuccessfullydeploytheproductionversionofoursitelocally.Theproductionversionishighlyoptimizedanddoesnotcomewithsourcemapsand

Deployingtheproductionversion

84

Page 85: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

otherthingslikehotcodereloadingthatwouldnotbebeneficialtoourendusers.

So,let'screateaproductiondeployofourapp.Builditusing:

npmrunbuild

Theoutputofthecommandtellsusthatsomeroutes( /and /blogarenowprerenderedasstaticHTML,while /blog/[id]willbeservedbytheNode.jsbackend.

Thenyoucanrun npmrunstarttostarttheproductionserverlocally:

npmrunstart

Deployingtheproductionversion

85

Page 86: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Visitinghttp://localhost:3000willshowustheproductionversionoftheapp,locally.

Deployingtheproductionversion

86

Page 87: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

DeployingonNowInthepreviouschapterwedeployedtheNext.jsapplicationlocally.

Howdowedeployittoarealwebserver,sootherpeoplecanaccessit?

OneofthemostsimplewaystodeployaNextapplicationisthroughtheNowplatformcreatedbyZeit,thesamecompanythatcreatedtheOpenSourceprojectNext.js.YoucanuseNowtodeployNode.jsapps,StaticWebsites,andmuchmore.

Nowmakesthedeploymentanddistributionstepofanappvery,verysimpleandfast,andinadditiontoNode.jsapps,theyalsosupportdeployingGo,PHP,Pythonandotherlanguages.

Youcanthinkofitasthe"cloud",asyoudon'treallyknowwhereyourappwillbedeployed,butyouknowthatyouwillhaveaURLwhereyoucanreachit.

Nowisfreetostartusing,withgenerousfreeplanthatcurrentlyincludes100GBofhosting,1000serverlessfunctionsinvocationsperday,1000buildspermonth,100GBofbandwidthpermonth,andoneCDNlocation.Thepricingpagehelpsgetanideaofthecostsifyouneedmore.

ThebestwaytostartusingNowisbyusingtheofficialNowCLI:

npminstall-gnow

Oncethecommandisavailable,run

DeployingonNow

87

Page 88: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

nowlogin

andtheappwillaskyouforyouremail.

Ifyouhaven'tregisteredalready,createanaccountonhttps://zeit.co/signupbeforecontinuing,thenaddyouremailtotheCLIclient.

Oncethisisdone,fromtheNext.jsprojectrootfolderrun

now

andtheappwillbeinstantlydeployedtotheNowcloud,andyou'llbegiventheuniqueappURL:

Onceyourunthe nowprogram,theappisdeployedtoarandomURLunderthe now.shdomain.

Wecansee3differentURLsintheoutputgivenintheimage:

https://firstproject-2pv7khwwr.now.shhttps://firstproject-sepia-ten.now.shhttps://firstproject.flaviocopes.now.sh

DeployingonNow

88

Page 89: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Whysomany?

ThefirstistheURLidentifyingthedeploy.Everytimewedeploytheapp,thisURLwillchange.

Youcantestimmediatelybychangingsomethingintheprojectcode,andrunning nowagain:

Theother2URLswillnotchange.Thefirstisarandomone,thesecondisyourprojectname(whichdefaultstothecurrentprojectfolder,youraccountnameandthen now.sh.

IfyouvisittheURL,youwillseetheappdeployedtoproduction.

DeployingonNow

89

Page 90: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

YoucanconfigureNowtoservethesitetoyourowncustomdomainorsubdomain,butIwillnotdiveintothatrightnow.

The now.shsubdomainisenoughforourtestingpurposes.

DeployingonNow

90

Page 91: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

AnalyzingtheappbundlesNextprovidesusawaytoanalyzethecodebundlesthataregenerated.

Openthepackage.jsonfileoftheappandinthescriptssectionaddthose3newcommands:

"analyze":"cross-envANALYZE=truenextbuild",

"analyze:server":"cross-envBUNDLE_ANALYZE=servernextbuild",

"analyze:browser":"cross-envBUNDLE_ANALYZE=browsernextbuild"

Likethis:

{

"name":"firstproject",

"version":"1.0.0",

"description":"",

"main":"index.js",

"scripts":{

"dev":"next",

"build":"nextbuild",

"start":"nextstart",

"analyze":"cross-envANALYZE=truenextbuild",

"analyze:server":"cross-envBUNDLE_ANALYZE=servernextbuild"

,

"analyze:browser":"cross-envBUNDLE_ANALYZE=browsernextbui

ld"

},

"keywords":[],

"author":"",

"license":"ISC",

"dependencies":{

"next":"^9.1.2",

"react":"^16.11.0",

Analyzingtheappbundles

91

Page 92: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

"react-dom":"^16.11.0"

}

}

theninstallthose2packages:

npminstall--devcross-env@next/bundle-analyzer

Createa next.config.jsfileintheprojectroot,withthiscontent:

constwithBundleAnalyzer=require('@next/bundle-analyzer')({

enabled:process.env.ANALYZE==='true'

})

module.exports=withBundleAnalyzer({})

Nowrunthecommand

npmrunanalyze

Analyzingtheappbundles

92

Page 93: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Thisshouldopen2pagesinthebrowser.Onefortheclientbundles,andonefortheserverbundles:

Analyzingtheappbundles

93

Page 94: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Analyzingtheappbundles

94

Page 95: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Thisisincrediblyuseful.Youcaninspectwhat'stakingthemostspaceinthebundles,andyoucanalsousethesidebartoexcludebundles,foraneasiervisualizationofthesmallerones:

Analyzingtheappbundles

95

Page 96: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Analyzingtheappbundles

96

Page 97: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

LazyloadingmodulesBeingabletovisuallyanalyzeabundleisgreatbecausewecanoptimizeourapplicationveryeasily.

SayweneedtoloadtheMomentlibraryinourblogposts.Run:

npminstallmoment

toincludeitintheproject.

Nowlet'ssimulatethefactweneeditontwodifferentroutes: /blogand /blog/[id].

Weimportitin pages/blog/[id].js:

importmomentfrom'moment'

...

constPost=props=>{

return(

<div>

<h1>{props.post.title}</h1>

<p>Publishedon{moment().format('ddddDMMMMYYYY')}</p>

<p>{props.post.content}</p>

</div>

)

}

I'mjustaddingtoday'sdate,asanexample.

ThiswillincludeMoment.jsintheblogpostpagebundle,asyoucanseebyrunning npmrunanalyze:

Lazyloadingmodules

97

Page 98: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Seethatwenowhavearedentryin /blog/[id],theroutethatweaddedMoment.jsto!

Itwentfrom~1kBto350kB,quiteabigdeal.AndthisisbecausetheMoment.jslibraryitselfis349kB.

Theclientbundlesvisualizationnowshowsusthatthebiggerbundleisthepageone,whichbeforewasverylittle.And99%ofitscodeisMoment.js.

Lazyloadingmodules

98

Page 99: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Everytimeweloadablogpostwearegoingtohaveallthiscodetransferredtotheclient.Whichisnotideal.

Onefixwouldbetolookforalibrarywithasmallersize,asMoment.jsisnotknownforbeinglightweight(especiallyoutoftheboxwithallthelocalesincluded),butlet'sassumeforthesakeoftheexamplethatwemustuseit.

Lazyloadingmodules

99

Page 100: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

WhatwecandoinsteadisseparatingalltheMomentcodeinaseparatebundle.

How?InsteadofimportingMomentatthecomponentlevel,weperformanasyncimportinside getInitialProps,andwecalculatethevaluetosendtothecomponent.Rememberthatwecan'treturncomplexobjectsinsidethe getInitialProps()returnedobject,sowecalculatethedateinsideit:

importpostsfrom'../../posts.json'

constPost=props=>{

return(

<div>

<h1>{props.post.title}</h1>

<p>Publishedon{props.date}</p>

<p>{props.post.content}</p>

</div>

)

}

Post.getInitialProps=async({query})=>{

constmoment=(awaitimport('moment')).default()

return{

date:moment.format('ddddDMMMMYYYY'),

post:posts[query.id]

}

}

exportdefaultPost

Seethatspecialcallto .default()after awaitimport?It'sneededtoreferencethedefaultexportinadynamicimport(seehttps://v8.dev/features/dynamic-import)

Nowifwerun npmrunanalyzeagain,wecanseethis:

Lazyloadingmodules

100

Page 101: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

Our /blog/[id]bundleisagainverysmall,asMomenthasbeenmovedtoitsownbundlefile,loadedseparatelybythebrowser.

Lazyloadingmodules

101

Page 102: Table of Contents - topidesta.files.wordpress.com · First, the page takes longer to become visible to the user, because before the content loads, all the JavaScript must load, and

WheretogofromhereThereisalotmoretoknowaboutNext.js.Ididn'ttalkaboutmanagingusersessionswithlogin,serverless,managingdatabases,andsoon.

ThegoalofthisHandbookisnottoteachyoueverything,butinsteaditaimstointroduceyou,gradually,toallthepowerofNext.js.

ThenextstepIrecommendistotakeagoodreadattheNext.jsofficialdocumentationtofindoutmoreaboutallthefeaturesandfunctionalityIdidn'ttalkabout,andtakealookatalltheadditionalfunctionalitiesintroducedbyNext.jsplugins,someofwhichareprettyamazing.

Wheretogofromhere

102