2022

@AtilaFassina

Day 2 — Going Deeper

@AtilaFassina

PERFORMANCE

BENCHMARK

⚠️ Only for /page routes.

@AtilaFassina

↪ Largest Contentful Paint

↪ First Contentful Paint

↪ Content Layout Shift

↪ First Input Delay

↪ Time To First Byte

↪ Interaction to Next Paint

web vitals

🧪

@AtilaFassina

INP

POOR

NEEDS IMPROVEMENT

GOOD

200ms
500ms

🐭 Clicking with a mouse.

📱 Tapping on a touchscreen.

⌨️ Pressing a key on keyboard.

The sum of response delay of every interaction during a page's lifecycle.

@AtilaFassina

next.js performance

Next.js-hydration

Next.js-route-change-to-render

Next.js-render

Time for the page to start and finish hydrating (ms)

Time for the page to start rendering after route change (ms)

Time for the page to finish rendering after route change (ms)

@AtilaFassina

export function reportWebVitals(metric) {
  const { label, name } = metric
}

↪ name: "FCP" | "LCP" | ...

↪ label: "custom" | "web-vital"

@AtilaFassina

PERFORMANCE

IMPROVEMENTS

@AtilaFassina

@AtilaFassina

debugging

export default {
  experimental: {
    webVitalsAttribution: ['CLS', 'LCP']
  }
}

next.config.mjs

@AtilaFassina

@AtilaFassina

server image optimization

↪ Improved Performance

↪ Visual Stability

↪ Faster Page Loads

size and format optimization

prevents CLS

lazy loading + placeholders

@AtilaFassina

@AtilaFassina

server image optimization

↪ Improved Performance

↪ Visual Stability

↪ Faster Page Loads

size and format optimization

prevents CLS

lazy loading + placeholders

@AtilaFassina

@AtilaFassina

server image optimization

import Image from 'next/image'
import photo from '~/public/photo.png'

function SuperImage() {
  return (
    <Image
      src={profilePic}
      alt="Picture of the author"
    />
  )
}

@AtilaFassina

@AtilaFassina

server image optimization

import Image from 'next/image'
import photo from '~/public/photo.png'

function SuperImage() {
  return (
    <Image
      priority
      src={profilePic}
      alt="Picture of the author"
    />
  )
}

for better Largest Contentful Paint

@AtilaFassina

version 12

import Image from 'next/image'
import Image from 'next/future/image'

X

<Image>

@AtilaFassina

version 12

import Image1 from 'next/image';
import Image2 from 'next/future/image';
import Image1 from 'next/legacy/image';
import Image2 from 'next/image';

<Image>

@AtilaFassina

version 13

import Image from 'next/legacy/image'
import Image from 'next/image'

X

<Image>

@AtilaFassina

Experimental Image Component

<Image>

↪ Less client-side JavaScript.

↪ Simplified styling.

↪ Better a11y.

@AtilaFassina

Experimental Image Component

<Image>

 layout 👉 style.

objectFit 👉 style.

objectPosition 👉 style.

↪ Removes lazyBoundary and lazyRoot

@AtilaFassina

NEXT.JS 13

EXPERIMENTS

@AtilaFassina

⚠️ Alpha Release.

⚠️ Only works on development.

next dev --turbo

↪ Written in Rust.

↪ The Fastest Compiler.

@AtilaFassina

@next/link

<Link href="/somewhere">
  <a>Somewhere</a>
 </Link>
<Link href="/somewhere">
  Somewhere
 </Link>

Then

Now

@AtilaFassina

@next/font

↪ Automatic self-hosting external fonts.

⚡️

🔒

export default {
  experimental: {
    fontLoaders: [
      { loader: '@next/font/google', options: { subsets: ['latin'] } }
    ]
  }
}
const inter = Inter({ subsets: ["latin"] })

next.config.mjs

route.jsx

@AtilaFassina

@next/font +

import type { AppProps } from 'next/app'
import '~/styles/global.css'
import { Jost } from '@next/font/google'
import defaultTheme from 'tailwindcss/defaultTheme'

const jost = Jost({
  subsets: ['latin', 'latin-ext'],
  weight: 'variable',
  display: 'optional',
  preload: true,
})

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <>
      <style jsx global>{`
        html {
          font-family: ${jost.style.fontFamily}, ${defaultTheme.fontFamily.sans};
        }
      `}</style>
      <Component {...pageProps} />
    </>
  )
}

/pages/_app.tsx

@AtilaFassina

@next/font +

import { type ReactNode } from 'react'
import { Jost } from '@next/font/google'

const jost = Jost({
  subsets: ['latin', 'latin-ext'],
  weight: 'variable',
  display: 'optional',
  preload: true,
  variable: '--font-jost'
})

export default function RootLayout({children }: { children: ReactNode }) {
  return (
    <html lang="en" className={jost.variable}>
      <body>{children}</body>
    </html>
  )
}

/app/layout.tsx

@AtilaFassina

⏰ 10 minutes

@AtilaFassina

AppDir

⚠️ 

/** @type {import('next').NextConfig} */
export default {
  reactStrictMode: true,
  experimental: {
    appDir: true,
  },
}

next.config.mjs

⚠️ ⚠️ ⚠️ ⚠️

@AtilaFassina

/app features

↪ Client vs Server Components.

↪ React Suspense.

↪ Layouts as First-Class.

↪ Data Mutation.

↪ use() React Hook.

↪ Route Groups.

↪ Edge-First.

↪ Parallel Routes

↪ Intercepting Routes

↪ Route Announcements.

@AtilaFassina

/app

🚫 AMP Support.

🚫 Internationalization (i18n).

🤨 what?

🤔 middleware + server components

👍 Internationalization (i18n).

Not coming...

@AtilaFassina

/app

🙏 Async Components TS support.

🙈 use() for client-side data fetching

🤨 waiting on React team

🤔 waiting on TypeScript team.

Really missing

@AtilaFassina

Hierarchy

Re-renders on navigation

Maintain across navigation

Suspense Boundary

Error Boundary

/segment

View components go here

@AtilaFassina

/app

@AtilaFassina

error boundary

@AtilaFassina

/app

@AtilaFassina

layout

export default function MyLayout({ children }: { children: React.ReactNode }) {
  return (
    <section>
      {children}
    </section>
  )
}

Shared

/dashboard
/dashboard/settings

@AtilaFassina

head.tsx

export default function Head() {
  return (
    <>
      <title>My Page Title</title>
      <meta name="viewport" content="width=device-width, initial-scale=1"  />
    </>
  )
}

↪ Replaces next/head.

@AtilaFassina

Root Layout

export default function RootLayout({ children }: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

* replaces both /pages/_app and /pages/_document

↪ Always a Server Component.

↪ Must have <html> and <body>.

@AtilaFassina

Root Layout

Replaces both /pages/_app and /pages/_document

import { type ReactNode } from 'react'
import { Jost } from '@next/font/google'

const jost = Jost({
  subsets: ['latin', 'latin-ext'],
  weight: 'variable',
  display: 'optional',
  preload: true,
  variable: '--font-jost'
})

export default function RootLayout({children }: { children: ReactNode }) {
  return (
    <html lang="en" className={jost.variable}>
      <body>{children}</body>
    </html>
  )
}

/app/layout.tsx

@AtilaFassina

route groups

not a route

@AtilaFassina

nesting layouts

@AtilaFassina

INTERLUDE

pages  x  app

@AtilaFassina

/pages Layout

import { UserInfoProvider } from '../context/user-info'
import { ExtDataProvider } from '../context/external-data-provider'
import { UserNavigationLayout } from '../layouts/user-navigation'
import { ErrorReporter } from '../components/error-reporter'
import { Loading } from '../components/loading'

export const MainUserManagement = (page) => {
  const { data, error } = useSWR('/api/endpoint')

  if (error) => <ErrorReporter {...error} />
  if (!data) => <Loading />

  return (
    <UserInfoProvider>
      <ExtDataProvider>
        <UserNavigationLayout>
          {page}
        </UserNavigationlayout>
      </ExtDataProvider>
    </UserInfoProvider>
  )
}

React Context to share data

@AtilaFassina

DO NOT HAVE

React Context

server components

@AtilaFassina

layout

↪ Layouts can make requests.

↪ React dedupes requests on rendering.

@AtilaFassina

ADVANCED

ROUTING

@AtilaFassina

route patterns

↪ Parallel Routes

↪ Conditional Routes

↪ Intercepting Routes

simultaneously show two or more pages in the same view

render a route based on a condition

intercept a route and show it in another route

@AtilaFassina

parallel routes

↪ Split views

↪ E.g.: Dashboards

@AtilaFassina

intercepting routes

↪ List all items while editing one.

↪ Photos in gallery.

Made with Slides.com