Next.js er i vinden som aldri før, og det populære React-rammeverket har nå over 3 millioner nedlastninger hver eneste uke.
Next.js 13 ble lansert i oktober i fjor med mange nye funksjoner og en helt ny App Router, som krever en del omskrivning. Hva er nytt? Og er det verdt å oppgradere?
De som allerede er godt kjent med Next.js kjenner nok til fordelene som server-side rendering, Static Site Generation, kort lastetid, forbedret SEO, bildeoptimalisering og mye mer.
Jeg skal ikke gå i detalj om hvordan Next.js fungerer og hvilke fordeler det gir, men heller se på hva som er de viktigste endringene fra Next.js 12 til Next.js 13.
Server Components
En av de største endringene er introduksjonen av Server Components, som erstatter getServerSideProps og getStaticProps.
For å kunne forstå hvordan det fungerer må vi ta en titt på Server Side Rendering (SSR) og Static Site Generation (SSG). Med SSG genereres statiske HTML-filer fra innholdet når nettsiden bygges. De statiske filene blir sendt til nettleseren og blir cachet, slik at de kan bli gjenbrukt for hver gang det blir gjort et nytt kall til nettleseren. SSR derimot vil generere ny HTML for hver request. Dermed kan man si at SSG gir deg en raskere responstid, mens SSR gjør det enklere å ha oppdaterte dynamiske sider.
«Kan det bli bedre enn dette? Vel, det finnes likevel noen ulemper.»
Så hva er egentlig annerledes med Server Components? Jo, ved å bruke Server Components er det mulig å bruke server-side logikk direkte i en komponent som blir rendret på serveren.
Det betyr at man kan hente data eller gjøre API-kall i en komponent og dermed redusere mengden JavaScript man sender til klienten. Dette vil igjen optimalisere ytelsen ved å redusere bundle-size og få direkte tilgang til serveren sin infrastruktur som f.eks. databaser og filsystem.
Kan det bli bedre enn dette? Vel, det finnes likevel noen ulemper med den nye funksjonaliteten. En server-komponent kan ikke bruke klient-funksjoner sånn som state-håndtering, hooks eller andre klient-events. Ønsker du å bruke disse funksjonene, er du nødt til å bruke ‘use client’ direktivet for å markere at dette ikke skal rendres på serveren. Det gjør du ved å skrive «'use client’» øverst i filen som inneholder komponenten din, og den vil da automatisk bli konvertert til en klient-komponent:
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
Når skal man bruke hva?
En enkel huskeregel for når man skal bruke de forskjellige er at Server Components er best å bruke når man skal hente data, eller hvis det ikke involverer noen form for brukerinteraksjon.
Client Components er best å bruke for alt som inkluderer brukerinteraksjon og krever bruk av state og hooks. Server Components og Client Components har hvert sitt bruksområde, og utfordringen er å finne ut hvordan man kan bruke disse to sammen.
For å optimalisere ytelsen enda mer anbefaler teamet bak Next.js å flytte Client Components så langt ned i komponenttreet som mulig. Et eksempel på dette er hvis du har en Layout.tsx som inneholder statiske elementer som bilder og tekst, sammen med et interaktivt søkefelt som bruker state.
Da kan du trekke ut den interaktive logikken inn i en egen Client Component som du døper til f.eks. SearchField.tsx, og beholde Layout.tsx som Server Component. SearchField.tsx kan da flyttes lenger ned i komponenttreet, og vi ser dermed tydelig her hvordan vi minimerer mengden JavaScript som sendes til klienten. Dette er et av flere designmønster man typisk vil benytte når man arbeider med Server Components.
App Router vs Pages Router
I Next.js 13 er Pages Router byttet ut med App Router og det introduserer en ny måte å strukturere filer og håndtere routing på. Den største forskjellen er at i stedet for at alle filer som opprettes i /pages-mappen er egne sider, så er hver eneste mappe som opprettes en egen rute. I hver mappe må du opprette en page.tsx-fil som erstatter index.tsx fra Next.js 12.
Eksempel på Routing med Next.js 13:
For øyeblikket støttes både App Router og Pages Router og begge kan brukes samtidig i et prosjekt. Layout.tsx er en UI-komponent som blir delt mellom de ulike sidene, som er veldig nyttig når flere sider dere mye av den samme designet.
I Next 13 er det også mulig å nøste layouts, bedre kalt Nested Layouts. Da oppretter du enkelt en egen layout.tsx inne i en mappe som feks app/[article]/layout.tsx som kan bli brukt på alle sider i den mappen.
Next/Image
I Next.js 13 ble det lansert et nytt og kraftig bildehåndteringsverktøy som gjør at du enkelt kan vise bilder på klienten med mye mindre javascript-bundle, det er lettere å tilpasse og konfigurere, bedre tilpasning for universell utforming og det laster fortere med native lazy loading.
I tillegg til dette har den nyeste oppdateringen oppgradert bildeoptimaliseringen, som gjør at bilder vi lastes raskere.
Next/Link
Med Next/Link slipper du nå å legge til manuelt inne i -elementet.
Lenker kan nå skrives så enkelt som dette:
<Link href="/about">
About
</Link>
Next.js 13 i praksis
Jeg har vært så heldig og fått jobbet i et prosjekt der teamet fikk lov til å skrive om fra Next.js 12 til Next.js 13.
Den første utfordringen som dukket opp etter oppgraderingen var at alle komponenter ble standard Server Components, med mindre du definerer spesifikt at det er en Client Component. Dette krever endringer i alle komponenter som bruker hooks, state eller andre klient-funksjoner.
En annen utfordring ved at alle komponenter var standard Server Components, var at tredjepartskode brakk og vi måtte jobbe oss gjennom feilmeldingene. Likevel var ofte løsningen å legge til «use client» øverst i filen og konvertere til Client Component.
Spørsmålet da er «men burde dette egentlig være en Server Component likevel?». Da kan det være lurt å se på denne tabellen før du konverterer:
(kilde)
Dersom en Server Component bruker klient-funksjoner som hooks fikk vi feilmeldingen «Error: Attempted to call the default export of /path/to/file/file.ts from the server but it's on the client». Next.js har støtte for Client Components i en Server Component, men ikke for at klient-funksjoner eller hooks blir brukt i Server Components.
Igjen er løsningen å bruke «use client» eller hvis du ikke ønsker å konvertere hele komponenten: flytt koden som bruker hooks inn i en child komponent og gjør om den til en Client Component.
Fra Pages Router til App Router
Selv om det er fullt mulig å bruke gamle Pages Router bestemte prosjektet seg for å skrive om til nye App Router. Valget ble tatt fordi Server Components støttes ikke i Page Router.
Her var det en utfordring å overføre den eksisterende strukturen og tenke mapper i stedet for filer. Vi støtte også på server-problemer og opplevde at hot reloading ikke virket som det skulle.
Det viste seg at vi hadde brukt async på en Client Component som var en veldig lett fiks, men vanskelig å forstå at det var problemet ut ifra feilmeldingen.
Selv om det var en litt humpete start og en ny måte å tenke på, opplevde vi etter hvert fort fremgang. Dokumentasjonen til Next er enkel å følge og vi så fort fordeler som rask lastetid og forbedret SEO.
Verdt tida det tar
Det er spennende, men samtidig utfordrende og tidkrevende å sette seg inn i nye oppdateringer i et rammeverk, men akkurat her må jeg skryte av teamet bak Next.js.
Jeg synes de har gjort en veldig god jobb med dokumentasjon, og enda viktigere det å lytte til tilbakemeldinger og kritikk fra brukerne sine. De lanserer hyppige versjonsoppdateringer og adresserer forbedringer og endringer på en ryddig og god måte.
Mange omfavner konseptet Server Components, mens andre mener det er forvirrende og unødvendig komplisert. I tillegg kunne det vært støtte for flere tredjeparts-løsninger, men dette vil nok forbedre seg på sikt.
Etter min mening er App Routing og Server Components noe vi bare har sett begynnelsen av, og det er absolutt verdt å lære seg konseptene først som sist. Selv om det kan ta litt tid, vil omskrivningen bringe med seg mange andre fordeler som gir deg en bedre bruker- og utvikleropplevelse.