Arafat Abdussalam
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    ## Introduction Web performance describes how fast or slow a web app is. It is crucial to application development. A highly optimized web app is quick, appeals to users' experience, and leads to improved product conversion rates and high search engine ranking. In this article, you will understand how the **Core Web Vitals (CWV)** metrics are affected by the resources used in your web app; you will implement best practices for improving the performance of your NextJs application, and understand how to measure the performance of your optimized web app. ## Overview of User Experience Users determine a product's success rate; their experience while navigating or interacting with a product's web app can indirectly affect the product's revenue. Building web pages that load faster when users request them is essential. Excluding the speed and user interactivity of a web app, it is also important that the contents displayed on the user interface of a web page maintain visual stability and that these contents do not always change their position, shifting from one space to the other. ## Factors Affecting Web Performance Certain resources can negatively impact web app performance if they are not used effectively. Let's examine each one. ### Multimedia File Size Image and video files take up much space in a web browser. When these files are of high quality, they become too large, resulting in slower load time and a shift in the position of other contents on the web page. The browser causes the shift after the files have been rendered because the browser cannot calculate the appropriate width and height of these files. This shift is known as **Cumulative Layout Shift (CLS)**, a CWV metric that determines if the surrounding contents of an image (or other elements) move to another position after the image (or element) has loaded. In some instances, multimedia files represent the main contents of a web page. When these files load slowly, they affect the web page's **Largest Contentful Paint (LCP)**. LCP determines how fast visually important web content is rendered. ### Network Requests Remote resources such as libraries, scripts, packages, and APIs requested from network servers are considered resource-blocking. This affects a web app's INP score. **Interaction to Next Paint (INP)** is also a CWV metric. It signifies the time it takes for web content to be rendered entirely after user interaction during request time. ### Memory Usage Large-scale applications require larger resources, which affects a web app's performance. To achieve optimized memory usage during build time, enable lazy loading, minimize the size of resources used, eliminate redundant code, enable caching, and analyze memory issues with tools such as Chrome DevTools. ### URL Redirect URL redirect means visiting a web page from another web page. These web pages have URL patterns that differ from one another. When these patterns do not match, an error occurs in the browser, leading users to view an unexpected web page. A URL redirect is useful when a web page has updated features or after form submission. When URL redirect is not implemented effectively, it can lead to slow load time and low search engine ranking of the web page. Let us implement the best practices for optimizing the resources mentioned above. ## 1. Display images with built-in `Image` component NextJs has a built-in `Image` component with props, making it easier to control how we want to render an image. Here is an explanation on the purpose of each prop: ### Required Props: * **`src`** (required): The source of an image. The image can be stored locally in the repository or remotely on a network server. * **`alt`** (required): The `alt` property provides textual information as an alternative to an image. It can be read aloud by screen-reader assistive technology, contributing to an accessible web app. The `alt` can be set to an empty value if the image does not add significant information to the user interface. * **`width`** (required for remote images): This determines how broad an image appears. * **`height`** (required for remote images): This determines the length of an image. ### Non-required props: Here are some non-required properties of the Next.js `Image` component. * **`priority`**: When an `Image` has a `priority` prop, the browser will pre-load the image before it is displayed, loading it faster. This improves the LCP score of a web app. * **`fill`**: The `fill` property indicates that the parent element determines an image's width and height. * **`sizes`**: This specifies the width and height of an image at different breakpoints of a user's device. * **`loader`**: A function that returns a URL string for an image. It accepts `src`, `width`, and `quality` as parameters. * **`placeholder`**: A `placeholder` fills the blank space of an image before it is rendered completely. * **`loading`**: Accepts a value of `{lazy}` to specify lazy-loading. * **`style`**: Enhances an image's visual appeal. Does not include other accepted `Image` props as its property. * **`onLoad`, `onError`**: Event handlers. Here, let us control how we want to render different images. ### Rendering Local Images ```js // flowerPage.jsx import Image from 'next/image' import roseFlower from '../public/roseImage.png' export default function FlowerImage() { return( <main> <div style={{width:"600px", height:"600px"}}> <Image src={roseFlower} alt="" style={{objectFit: "contain"}} fill priority /> </div> </main> ) } ``` View: ![render local images - nextjs optimization.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/render_local_images_nextjs_optimization_ed87f7b1cc.png) In the `flowerPage.jsx` file above, we did not specify the `height` and `width` inside the `Image` component since `roseFlower` is a local image. NextJs automatically calculates the `width` and `height` of a locally stored image. The `roseFlower` is prioritized during load time with the `priority` prop. However, the `width` and `height` of the `roseflower` are determined by its parent element using the `fill` prop. ### Rendering Remote Images Specifying the `width` and `height` of an image rendered from an external source is required. This gives us more control over the space that will be occupied by the image before it is rendered. ```js // monalisaPage.jsx import Image from 'next/image' export default function MonalisaImage() { return( <Image src="https://s3.amazonaws.com/my-bucket/monalisa.webp" alt="" height={600} width={600} /> ) } ``` Images rendered from external sources can affect the security of a web app. To ensure that a web app renders images from specified URLs only, we have to include `remotePatterns` in our `next.config.js` file. `remotePatterns` accepts an object of `protocol`, `hostname`, `port`, and `pathname` Let us configure our `next.config.js` file to render images from `https://s3.amazonaws.com/my-bucket/**` URL paths only, with different possible number of path segments or subdomains at the end: ```js // next.config.js module.exports = { images: { remotePatterns: [ { protocol: 'https', hostname: 's3.amazonaws.com', port: '', pathname: '/my-bucket/**' }, ], }, } ``` To specify path segments or subdomains at the beginning of the URL, place `**` at the start of the URL paths. For example, `hostname: '**.amazonaws.com'` means that `s3` can be replaced with another subdomain or path segment. Use `*` instead of `**` to allow a single path segment or subdomain. ### Rendering Images using Next.js `loader` Function The `loader` function accepts `src`, `width` and `quality` as parameters while generating dynamic URLs for an image. It works in client components only. You can use `loader` for local images or remote images. Here is an example of how to use `loader`: ```js // artsCollectionPage.jsx 'use client' import Image from 'next/image' const artsImageLoader = ({src, width, quality}) => { return `https://example.com/${src}?w=${width}q=${quality}` } export default function ArtsCollectionImage() { return( <Image loader={artsImageLoader} src='../public/roseImage.png' alt="" width={600} height={600} quality={80} /> ) } ``` ### Adding `placeholder` to Images Placeholder solves slow network connectivity issues for image rendering. Here in the `artsGalleryPage.jsx` file, the image loads with a blurry effect before it is fully rendered: ```js // artsGalleryPage.jsx import Image from 'next/image' import picasso from '../public/picasso.jpg' export default function ArtsGallery() { return( <Image src={picasso} alt="" placeholder='blur' loading='lazy' /> ) } ``` ![blur image - nextjs optimization.gif](https://delicate-dawn-ac25646e6d.media.strapiapp.com/blur_image_nextjs_optimization_2c474a1ad2.gif) In the code above,`loading='lazy'` enables delayed image rendering. Lazy loading images is useful for images, not the LCP for a web page. The `placeholder` can equally be set to `'empty'` or `'data:image/..'`. 1. **Using a Separate Image as `placeholder`** When the `placeholder` is set to `data:image/...`, the `src` image loads after the `placeholder` image, which is an image data type with a URI converted to `base64`. This is useful when the `placeholder` image has dominant colors that blend with the `src` image. It keeps users on the web page without feeling the need to wait while the `src` image loads. ```js placeholder='data:image/<jpeg|png|...>;base64,<data-uri>' ``` For example: ```js // artsGalleryPage.jsx import Image from 'next/image' import picasso from '../public/picassoImage.jpeg' export default function ArtsGallery() { return( <Image src={picasso} alt="" placeholder='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAH0AfQDAREAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAIF/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AobEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9k=' loading='lazy' /> ) } ``` ![Using separate image - nextjs optimization.gif](https://delicate-dawn-ac25646e6d.media.strapiapp.com/Using_separate_image_nextjs_optimization_cd58f8c3fb.gif) 2. **Set the Separate image `placeholder` To Have Blurry Effect** To blur a placeholder with an image data type, use `blurDataURL` prop and the `placeholder` prop. ```js placeholder='blur' blurDataURL='data:image/jpeg;base64,<data-uri>' ``` For example: ```js // artsGalleryPage.jsx import Image from 'next/image' import picasso from '../public/picassoImage.jpeg' export default function ArtsGallery() { return( <Image src={picasso} alt="" placeholder='blur' blurDataURL='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAH0AfQDAREAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAIF/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AobEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9k=' loading='lazy' /> ) } ``` ![Set the Separate image placeholder To Have Blurry Effect - nextjs optimization.gif](https://delicate-dawn-ac25646e6d.media.strapiapp.com/Set_the_Separate_image_placeholder_To_Have_Blurry_Effect_nextjs_optimization_8e3ddec8ce.gif) > **NOTE:** Alternatively, you can automatically generate placeholders using [plaiceholder](https://www.npmjs.com/package/plaiceholder) ### Display an Image Across Different Viewports Using `sizes` Property. In the example below, using media queries, the `lilacImage` renders at different sizes based on the user's screen size. ```js // lilacPage.jsx export default function lilacImage() { return( <Image src='../public/lilacImage.webp' alt="" sizes= "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" /> ) } ``` ## 2. Enhance Video Players with `<video>` and `<iframe/>` You can render video players in two different ways: using the `<video>` HTML tag for locally stored videos or, alternatively, using the`<iframe/>` HTML tag for remote videos requested from network servers. ### Rendering local videos with `<video>` `<video>` accepts the `width` and `height` properties to specify the space the video player occupies. To indicate the source of the video file, use the `src` attribute inside the `<source />` tag. The `control` attribute inside the `<video>` tag enables keyboard navigation and screen reader accessibility features. The `<track />` tag helps to provide alternative information such as `captions`, `subtitles`, `descriptions`, `chapters`, or `metadata` for a video player, these are specified with the `kind` attribute. To specify the source of the track file, use the `src` attribute. The textual information within the `<video>...</video>` tag is a fallback content. It keeps the users engaged in the web page. ```js // flowerMakingVideo.jsx export default function FlowerMakingVideo() { return( <video width="600" height="500" controls preload="none"> <source src="../flowerMakingVideo.mp4" type="video/mp4" /> <track src="../flowerMakingCaptions.vtt" kind="subtitles" srcLang="en" label="English" /> This video is not supported by your browser </video> ) } ``` ### Rendering remote videos with `<iframe/>` and React `Suspense` In NextJs, remote videos are first generated on the server. To render remote video players, use the `<iframe />` HTML tag. The video player is rendered without a border in the `artsMakingVideo.jsx` file below. The `title` attribute enables the screen reader to associate the video player with the information it provides. ```js // artsMakingVideo.jsx export default function ArtsMakingVideo() { return( <iframe width="600" height="600" src="https://www.youtube.com/..." frameborder="0" loading="lazy" title="art making video" allowfullscreen /> ) } ``` When a video loads, it is not interactive until the JavaScript for the file is equally loaded or fetched. This process is known as Hydration. It leads to slow response time when users need to interact with the video, causing the web app to have a poor INP score. React `Suspense` solves the hydration problem by providing fallback content for videos, leading to improved user experience. Let us better understand how to use`Suspense`. `Suspense` is a React component. It enables you to load an alternative layout that the video replaces with the `fallback` prop before the video is rendered completely. Let us create a fallback component: ```js // artsVideoFallback.jsx export default function ArtsVideoFallback() { return( <div> <p>Loading Arts Video</p> <p>Rather, just view me</p> </div> ) } ``` Wrap the `<iframe/>` tag inside the React `<Suspense>...</Suspense>` component ```js // artsMakingVideo.jsx import { Suspense } from "react"; import ArtsVideoFallback from "artsVideoFallback.jsx"; export default function ArtsMakingVideo() { return ( <Suspense fallback={ArtsVideoFallback}> <iframe // ... /> </Suspense> ); } ``` ## 3. Eliminate External Fonts download Fonts loaded from network servers take a long time to render. NextJs self-hosts Google fonts and local fonts without the need to render fonts from external sources. Nextjs fonts are functions called with an object of different properties: * **`src`** (required in local fonts): The path where the local font file is stored. * **`declarations`** (local fonts only): Describes generated font face. * **`subsets`** (google fonts only): An array of strings. It is useful for preloaded font subsets. * **`axes`** (google fonts only): Specifies the axes of variable fonts. * **`weight`**: Represents `font-weight`. * **`style`**: Represents `font-style`. Can be set to `italic`, `oblique` or `normal`. * **`display`**: Possible string value of `auto`, `block`, `swap`, `fallback` or `optional`. * **`preload`**: Specifies whether a font will preload. Sets to `true` or `false`. * **`fallback`**: An array of strings. Replace the imported fonts when a loading error occurs. To style a `fallback`, use a CSS class selector for the element it is applied to. * **`adjustFontFallback`**: Reduces the effect of font fallback on Cumulative Layout Shift (CLS). Sets to `true` or `false`. * **`variable`**: A string value of the declared CSS variable name. ### Using local Fonts Local fonts are downloaded fonts. You can save local font files in a `./styles/fonts/` folder in the project's root directory. To use local fonts, import `localFont` from `next/font/local`: ```js // app/layout.js import localFont from 'next/font/local' const myFont = localFont({ src: './fonts/my-font.woff2', style: 'italic', display: 'swap', fallback: ['arial'], }) export default function RootLayout({ children }) { return( <html lang="en" className={myFont.className} > <body>{children}</body> </html> ) } ``` ### Using Google Fonts Google fonts are classified into different types. When using a non-variable font, it is important to specify its `weight`. To use Google fonts, import the font type from `next/font/google`: ```js // app/layout.js import { Roboto } from next/font/google const roboto = Roboto({ weight: '400', subsets: ['latin'], style: ['normal', 'italic'], display: 'swap', }) export default function RootLayout({ children }) { return ( <html lang="en" className={roboto.className}> <body>{children}</body> </html> ) } ``` ### Rendering and Reusing Multiple Fonts To use multiple fonts in a reusable manner, call the fonts in a single fonts file as an `export const`: ```js // app/fonts.js import localFont from 'next/font/local' import { Roboto_Mono } from 'next/font/google' export const myLocalFont = localFont({ src: "my-local-font.tff", subset: ['latin'], }) export const roboto_mono = Roboto_Mono({ subsets: ['latin'], display: 'swap', }) ``` Next, render the font in the file to which you want to apply it. In the example below, the font is only rendered in the `artsCollectionPage.jsx` file: ```js // artsCollectionPage.jsx import { myLocalFont } from '../fonts.js' export default function ArtsCollection() { return( <div className={myLocalFont.className}> Available Arts </div> ) } ``` ### Using CSS variables to render fonts In the example below, to apply font to a specific text using a CSS variable, set the `className` of the text's parent element to the font's `variable`: ```js // aboutArtsPage.jsx import styles from './aboutArts.module.css' import { Inter } from 'next/font/google' const inter = Inter({ variable: '--font-inter', }) export default function AboutArts() { return( <div className=`${inter.className}`> <h1 className=`${styles.text}`>Arts help to improve the memory</h1> </div> ) } ``` Next, style the text in the `aboutArts.module.css` file: ```js // aboutArts.module.css .text { font-family: var(--font-inter) } ``` ### Using TailwindCSS to render Fonts Here, the `inter` and `roboto_mono` fonts are called with the variable `--font-inter` and `--font-roboto-mono`, respectively, in the `aboutArtistPage.jsx` file. ```js // aboutArtistPage.jsx import { Inter, Roboto_mono } from 'next/font/google' const inter = Inter({ variable: '--font-inter' }) const roboto_mono = Roboto_mono({ variable: '--font-roboto-mono' }) export default function AboutArtist() { return ( <section className=`${inter.variable} ${roboto_mono.variable}`> <h1>About Artist</h1> </section> ) } ``` To apply the CSS variable fonts using Tailwind CSS, add the css variable to your `tailwind.config.js` file: ```js // tailwind.config.js /** @type {import('tailwindcss').Config} */ module.exports = { content: [ 'pages/**/*.{js,ts, jsx,tsx}', 'components/**/*.{js,ts,jsx,tsx}', 'app/**/*.{js,ts,jsx,tsx}/' ], theme: { extend: { font-family: { sans: ['var(--font-inter)'], mono: ['var(--font-roboto-mono)'], }, }, }, plugins: [], } ``` ### Aplying fonts with `style` You can equally apply fonts to elements using `style`: ```js <h1 style={inter.className}>Well done</h1> ``` ## 4. Amplify Search Engine ranking with `metadata` Metadata provides additional information about the data in a web app. These data include documents, files, images, audio, videos, and web pages. When a web app has enriched metadata information, it takes high precedence and relevance over other web apps on search engines. In NextJs, metadata is classified as either static or dynamic. Dynamic metadata provides information bound to change, such as the current route parameter, external data, or `metadata` in parent segments. You can add metadata either through configuration or special files: ### Adding metadata through configuration For static metadata, you can export it using NextJs' built-in `metadata` object, while you can export dynamically generated metadata with changing values using the built-in `generateMetadata()` function. Both the `metadata` object and `generateMetadata()` function are exported in either `layout.js` or `page.js` files and can only be used for server components. * **Adding Static Metadata with `metadata` object** ```js // page.jsx export const metadata = { title: "....", description: "....", } export default function Page(){} ``` * **Adding Dynamic Metadata with `generateMetadata()` Function** The `generateMetadata()` function accepts `props` object and `parent` as parameters. The `props` object includes `params` and `searchParams`. The **`params`** contains the dynamic route parameters from the root segment to the segment where `generateMetadata()` is called. The **`searchParams`**: Contains the search params of the current URL. And the **`parent`** parameter is the resolved metadata's promise from the parent route segments. Below is an example: ```js // artist/[id]/page.jsx export async function generateMetadata({params, searchParams}, parent) { const id = params.id const artist = await fetch(`https://../${id}`).then((res)=>res.json()) const price = await parent return { title: artist.title, description: artist.description, }, } export default function ArtsPage({params, searchParams}){} ``` ## 5. Loading Scripts with `Script` Component NextJs has a built-in `Script` component that enables us to control how to render scripts intended for either a specific folder layout or the app root layout. To optimize performance, render the scripts in the specific folders' layouts where they are needed. Scripts can equally be loaded in `Page` files. Specifying the `id` prop in `Script` is useful for optimization purpose. ### Loading inline Scripts Inline scripts are written directly in the `Script` component. The `id` prop is required in inline scripts. Inline scripts can be written in two ways: * **With curly braces:** ```js // app/artsCollection/layout.jsx import Script from 'next/script' export default function Layout({children}) { return ( <div> <h1>Available Arts</h1> <section>{children}</section> <Script id="show-arts-collection"> { document.getElementbyId=('arts-collection').classList.remove('hidden') } </Script> </div> ) } ``` * **With `dangerouslyStyleInnerHTML` prop:** ```js // app/artsCollection/layout.jsx import Script from 'next/script' // ... <Script id="show-arts-collection" dangerouslySetInnerHTML={{ __html: "document.getElementById('arts-collection').classList.remove('hidden')", }} /> // ... ``` ### Loading External Scripts External scripts are loaded with a required `src` prop to specify the URL. ```js // ... <Script src="https://example.com" /> // ... ``` ### Specifying How Scripts Should Load Using the `strategy` Prop Although the `Script` is loaded only once in the web app, you can control how it loads with the following loading strategies: * `beforeInteractive`: The script will load before the NextJs code loads and before page hydration happens. * `afterInteractive`: The script will load immediately after page hydration happens. * `lazyOnLoad`: The script wll load in a delayed manner after every other code have been loaded in the browser. * `worker`: The script will load in a web worker. Here, render the script before page hdration occurs: ```js <Script src="https://example.com/script.js" strategy="beforeInteractive" /> ``` ### Adding Event Handlers to Scripts To control how a web page responds to certain events, you can use the following event handlers which can only be used in client components: * **`onLoad`**: Responds inmediately the script has finished loading. * **`onReady`**: This function responds after the script has finished loading and the component is fully displayed. * **`onError`**: Responds when an error occurs with script loading. #### Using Event Handlers ```js 'use client' import Script from 'next/script' // ... <Script src="..." onReady=()=> {console.log('users can interact with the component now')} /> // ... ``` ## 6. Implement URL Redirects Implement URL redirect with NextJs built-in functions and hooks based on different use cases. ### Redirecting Users After Mutations or Events Mutation involves updating data to a network server. Use `redirect` or `permanentRedirect` to enable URL redirect in server components, actions, or route handlers. #### Using `redirect` function `redirect` is called outside the `try/catch` block. `redirect` accepts two parameters: * **`path`**: The URL path users are redirected to. The path can be relative or absolute. * **`type`**: The URL redirect we want to enable is either to replace a URL pattern with `replace` or to push to an existing URL pattern with `push`. NextJs automatically uses the `push` redirect type only for server actions. Redirects that called in other files are `replace` by default. ```js // bidArtsAction.js 'user server' import { redirect } from 'next/navigation' import { revalidatePath} from 'next/cache' export default async function bidArts(id) { try{ // ... } catch(error){ // ... } revalidatePath('/bidArts') redirect(`/bidArts/${id}`) } ``` As shown in the example above, `revalidatePath` will update the cached `bidArts` page. Once the user `bidArts' server action is called, the URL pattern will change from `../bidArts` to `../bidArts/[id]`. If you want to replace the URL path, use: ```js redirect(`/bidArts/${id}`, replace) ``` #### using `permanentRedirect` function To redirect users to a permanently changed URL, use `permanentRedirect`: ```js // updateArtistPaintingAction.js 'use server' import { permanentRedirect} from 'next/navigate' import revalidateTag from 'next/cache' export async function updateArtistPainting(artsWork, formData){ try{ // ... } catch(error) { // ... } revalidateTag('artsWork') permanentRedirect(`artist/${artsWork}`) } ``` `revalidateTag` will update all paths related to `artsWork`. ### Redirecting Users Within Client components With `useRouter` hooks `useRouter` hook works in client components only. ```js // modernArts.jsx 'use client' import useRouter from 'next/navigation' export default function Page(){ const router = useRouter() return( <button onClick=(()=>{router.push(/fernado)})> Fernado </button> ) } ``` Here, once a user clicks on the button, the user is redirected to a new URL with the path `/fernado` ### Redirecting Users With `redirects` in `next.config.js` To enable URL redirect to different URL paths based on different incoming URL requests, include `redirects` to the `next.config.js` file. It allows you manage different number of URL paths at once. ```js // next.config.js module.exports = { async redirects() { return [ { source: '/about', destination: '/', permanent: true, }, { source: '/artists/:slug', destination: '/artsCollection/:slug', permanent: true, }, ], }, } ``` ### Redirecting users based on certain conditions using `NextResponse.redirect` Enabling URL redirects in Middleware allows the web browser to begin redirecting URLs before a user's request is completed, for example, before a browser completes an authentication process. Here, we want to redirect a user to the login page if the user is not authenticated. The redirect is processed before the login page is rendered. This will avoid errors when the page loads since we redirect the user after a condition has been met. ```js // middleware.js import { NextResponse} from 'next/server' import 'authenticate' from 'next/auth-provider' export function middleware(request) { const isAuthenticated = authenticate(request) if(!isAuthenticated) { NextResponse.redirect(new URL('/login', request.url)) } export const config = { matcher: '/artscollection/:path*' } ``` ## 7. Bundling and Analyzing packages with `@next/bundle-analyzer` To minimize memory usage, you can bundle the packages used in your web app with a bundler. A bundler automatically merges all the code written in the web app into a single file. This helps to solve dependency and latency issues. Certain resources depend on other resources, such as remote libraries, components, and frameworks, and are complex to manage. Latency is a measure of the distance in time between a user's device and the network server that is being requested. NextJs has a built-in plugin `@next/bundle-analyzer` which identifies and reports dependencies issues. ### Installing `@next/bundle-analyzer` plugin To install the the plugin, use: ```js npm i @next/bundle-analyzer # or yarn add @next/bundle-analyzer # or pnpm add @next/bundle-analyzer ``` ### `@next/bundle-analyzer` usage To use `@next/bundle-analyzer`: ```js // next.config.js /** @type {import('next')}.NextConfig */ import('next').NextConfig const nextConfig = { experimental: { optimizePackageImports: ['icon-library'], }, serverExternalPackages: ['package-name'], } const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true' }) module.exports = withBundleAnalyzer(nextConfig) ``` * **`optimizePackageImports`** enables manual optimization of the packages imported. This allows the packages to be imported once and used in different components as many times as possible. * **`withBundleAnalyzer`** includes the bundle analyzer's settings to the `next.config.js` file after it has been installed. * **`serverExternalPackages`** excludes `package-name` from being bundled in the application. The `next.config.js' file optionally uses `optimizePackageImports`, `withBundleAnalyzer`, and `serverExternalPackages`. ### Identifying and Reporting Bundles To analyze and generate reports on your bundles, use: ```js ANALYZE=true npm run build # or ANALYZE=true yarn build # or ANALYZE=true pnpm build ``` ## 9. Implement Lazy Loading Strategies Lazy loading is a performance strategy that reduces page load time by rendering web pages lightweight until users actively navigate to certain components. It is useful for enjoyable scrolling engagements. So far, in this article, you have implemented lazy loading for images and videos. Server components have features that enable automatic render delay. To enable manual lazy loading in server components, implement Streaming. Client components cannot be delayed automatically. You must use `dynamic` imports or `Suspense` to achieve lazy loading in client components. In this section, you will lazy load client components only: #### Using `dynamic` `dynamic` is a callback function that returns the components we want to lazy load as imports. To disable pre-rendering in client components, set the `ssr` option to `false`. To enable custom loading in `dynamic` imports, set the `loading` option to a preferred UI: ```js // artsExhibitionPage.jsx 'use client' import dynamic from 'next/dynamic' // client components: const Leonardo = dynamic(()=> import('../ui/leonardo')) const Picasso = dynamic(()=>import('../ui/picasso')) const Michelangelo = dynamic(()=>import('../ui/michelangelo'), {ssr: false}) const Magritte = dynamic(()=>import('../ui/magritte'), { loading: () => <div>While waiting for Magritte's arts work, just view me instead</div> }) export default function ArtsExhibition() { return ( <div> <Leonardo /> <Picasso /> <Magritte /> <Michelangelo /> </div> ) } ``` ![dynamic rendering - nextjs oprimization.gif](https://delicate-dawn-ac25646e6d.media.strapiapp.com/dynamic_rendering_nextjs_oprimization_ae8d3fa649.gif) #### Loading named exports with `dynamic` ```js // vermeer.js export function Vermeer() { return( <div>...</div> ) } ``` To lazy load the named export `Vermeer` using `dynamic`, return it as a Promise after it has been imported: ```js // artsExhibitionPage.jsx 'use client' const Vermeer = dynamic(import('../components/vermeer').then((res)=>res.Vermeer)) ``` ### Using `Suspense` With `Suspense`, you can lazy load client components while providing a `fallback` component: ```js // upcomingArtsExhibitionPage.jsx 'use client' import { Suspense} from 'react' import Leonardo from '../components/leonardo.jsx' import Picasso from '../components/picasso.jsx' export default function UpcomingArtsExhibition() { return( <Suspense fallback={<div>Waiting for all components to load at once</div>}> <Leonardo /> <Picasso /> </Suspense> ) } ``` ### Lazy loading packages or libraries To load packages or libraries only when needed, use the `await` import: ```js // myPage.jsx 'use client' import { useState } from 'react' export default function MyPage() { const [results, setResults] = useState(false) return( <div> <button onClick={async()=>{ const MyLibrary = (await import('my-library.js')).default const myLibrary = new MyLibrary() setResults(myLibrary) }> Show Result </button> <div>Results: {JSON.stringify(results)}</div> </div> ) } const myLibrary = (await import('my-library.js')).default ``` In the example above, `my-library.js` will load only when the `<button>` is clicked. ### Solving lazy loading problem with caching When lazy loading complex components, different problems may arise. An example is multiple requests from network servers at the same time. To prevent this problem, cache the components. Read about [Caching in NextJs](https://nextjs.org/docs/app/building-your-application/caching). ## Measuring Web Performance in Next.js In NextJs, you can always keep track of performance issues in production using: * **`reportWebVitals`[ hook](https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals)**: monitors Core Web Vitals (CWV) metrics and sends observed reports manually. * **Vercel's built-in observability [tool](https://vercel.com/docs/observability)**: integrated with other observability tools such as OpenTelementry, Datadog, through a process known as instrumentation. * **Google Lighthouse**: [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/) measures and provides reports on the score of different CWV metrics based on the URL of each web page, with suggested ways to fix the performance issues. ## Conclusion In this article, you have understood how different resources affect the user experience of a web app. You have also implemented techniques to optimize images, videos, fonts, metadata, URL redirects, scripts, and packages. You have also implemented lazy loading strategies for client components and other resources. Optimization enables your NextJs web app to be highly performant, lightweight, and enjoyable for users. You have also learned about different performance metrics and possible ways to measure your web app's performance.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully