2022
@AtilaFassina
Day 2 — Going Deeper
@AtilaFassina
⚠️ Only for /page routes.
@AtilaFassina
🧪
@AtilaFassina
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-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
}
@AtilaFassina
@AtilaFassina
@AtilaFassina
export default {
experimental: {
webVitalsAttribution: ['CLS', 'LCP']
}
}
@AtilaFassina
@AtilaFassina
size and format optimization
prevents CLS
lazy loading + placeholders
@AtilaFassina
@AtilaFassina
size and format optimization
prevents CLS
lazy loading + placeholders
@AtilaFassina
@AtilaFassina
import Image from 'next/image'
import photo from '~/public/photo.png'
function SuperImage() {
return (
<Image
src={profilePic}
alt="Picture of the author"
/>
)
}
@AtilaFassina
@AtilaFassina
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
import Image from 'next/image'
import Image from 'next/future/image'
X
@AtilaFassina
import Image1 from 'next/image';
import Image2 from 'next/future/image';
import Image1 from 'next/legacy/image';
import Image2 from 'next/image';
@AtilaFassina
import Image from 'next/legacy/image'
import Image from 'next/image'
X
@AtilaFassina
@AtilaFassina
layout
👉 style
.objectFit
👉 style
.objectPosition
👉 style
.lazyBoundary
and lazyRoot
@AtilaFassina
@AtilaFassina
⚠️ Alpha Release.
⚠️ Only works on development.
next dev --turbo
™
@AtilaFassina
<Link href="/somewhere">
<a>Somewhere</a>
</Link>
<Link href="/somewhere">
Somewhere
</Link>
Then
Now
@AtilaFassina
⚡️
🔒
export default {
experimental: {
fontLoaders: [
{ loader: '@next/font/google', options: { subsets: ['latin'] } }
]
}
}
const inter = Inter({ subsets: ["latin"] })
@AtilaFassina
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} />
</>
)
}
@AtilaFassina
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>
)
}
@AtilaFassina
@AtilaFassina
⚠️
/** @type {import('next').NextConfig} */
export default {
reactStrictMode: true,
experimental: {
appDir: true,
},
}
⚠️ ⚠️ ⚠️ ⚠️
@AtilaFassina
@AtilaFassina
🤨 what?
🤔 middleware + server components
@AtilaFassina
🤨 waiting on React team
🤔 waiting on TypeScript team.
@AtilaFassina
Re-renders on navigation
Maintain across navigation
Suspense Boundary
Error Boundary
/segment
View components go here
@AtilaFassina
@AtilaFassina
@AtilaFassina
@AtilaFassina
export default function MyLayout({ children }: { children: React.ReactNode }) {
return (
<section>
{children}
</section>
)
}
Shared
/dashboard
/dashboard/settings
@AtilaFassina
export default function Head() {
return (
<>
<title>My Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</>
)
}
next/head
.@AtilaFassina
export default function RootLayout({ children }: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
* replaces both /pages/_app
and /pages/_document
<html>
and <body>
.@AtilaFassina
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>
)
}
@AtilaFassina
not a route
@AtilaFassina
@AtilaFassina
@AtilaFassina
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
@AtilaFassina
@AtilaFassina
@AtilaFassina
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
@AtilaFassina