Slide 1

Slide 1 text

Sustainability in Web Dev How to Optimize React Apps? Jussi Pohjolainen

Slide 2

Slide 2 text

Key Points

Slide 3

Slide 3 text

Key Points • Bundle Size and Network Usage • Green Cloud Services • Frontend Optimization • Backend Optimization • Monitoring and Analytics

Slide 4

Slide 4 text

Bundle Size and Network Usage • Smaller Bundles: Smaller files require less energy for transmission and storage. This reduces the load on data centers, networks, and user devices. • Efficient Network Usage: Optimizing data transfer reduces the energy demand of the network infrastructure, including routers, switches, and undersea cables. • Use lazy loading and tree-shaking. • Optimize images and serve modern formats (e.g., WebP/AVIF). • Implement code splitting. • Use Content Delivery Networks (CDNs) for localized distribution.

Slide 5

Slide 5 text

Green Cloud Services • Green Cloud Providers • Use cloud providers committed to renewable energy and carbon neutrality (e.g., AWS with its sustainability initiatives, Google Cloud, or Azure with green energy offsets). • Energy Efficiency • Choose data centers near your user base to reduce latency and energy used for data transmission.

Slide 6

Slide 6 text

Frontend Optimization • Design lightweight websites that load quickly and reduce CPU usage on user devices, especially mobile. • Static Sites: Consider static site generation (e.g., Next.js, Hugo) to reduce server load. • Caching: Leverage browser caching and service workers for offline support. • Dark Mode: Reduce energy use, particularly on OLED screens. • Minimalist Design: Simplify interfaces to use fewer assets.

Slide 7

Slide 7 text

Backend Optimization • Server Efficiency: Optimize API calls and avoid redundant requests. • Database Queries: Minimize heavy queries and cache results where appropriate. • Serverless Architectures: Dynamically allocate resources to avoid always-on server energy consumption.

Slide 8

Slide 8 text

Monitoring and Analytics • Use tools like Website Carbon Calculator to measure and track your site's carbon footprint. • Chrome Dev Tools • Next.js analyzer

Slide 9

Slide 9 text

In Overall • Energy Efficiency: Optimize code, server infrastructure, and asset delivery to minimize energy consumption and carbon emissions. • Performance Optimization: Build fast-loading, resource-efficient websites using techniques like caching, compression, and lazy loading. • Sustainable Hosting: Choose hosting providers powered by renewable energy or with carbon offset programs. • https:!//www.thegreenwebfoundation.org/tools/directory/ • Inclusive and Minimal Design: Create accessible, lightweight websites optimized for all devices and internet speeds.

Slide 10

Slide 10 text

React App Optimization

Slide 11

Slide 11 text

Key Areas for Optimization 1. Efficient data fetching (SSR and CSR) 2. Reducing bundle size 3. Reducing number of renders 4. Monitoring performance using tools

Slide 12

Slide 12 text

CSR Step Interaction Description 1. Browser -> Server Request HTML + JS The browser sends a request to the server for the initial page load. 2. Server -> Browser Sends HTML Skeleton + JS The server responds with a minimal HTML file and associated JavaScript files. 3. Browser Loads and Executes JavaScript The browser parses the HTML, downloads, and executes JavaScript files. 4. Browser -> API Fetches Data The JavaScript running in the browser makes API calls to fetch dynamic data. 5. API/Backend -> Browser Sends Data Response The backend API responds with the requested data in JSON or other formats. 6. Browser Renders Content The JavaScript dynamically updates the DOM using fetched data.

Slide 13

Slide 13 text

SSR Step Interaction Description 1. Browser -> Server Request HTML The browser sends a request to the server for the page. 2. Server -> Browser Sends Fully Rendered HTML The server responds with the HTML content already rendered. 3. Browser Parses and Displays HTML The browser parses the HTML and displays the fully rendered page. 4. Browser -> Server Downloads JS (for hydration) The browser requests JavaScript files for hydration (making the app interactive). 5. Browser Executes JS (Hydrates App) The browser executes JavaScript to bind event listeners and make the app interactive.

Slide 14

Slide 14 text

Client Side Rendering (CSR) • Increases time to interactive (TTI) since data is fetched after the initial page load. • Poor SEO (Search Engine Optimization) because search engines may not execute JavaScript to fetch the data. • Higher client-side resource usage, affecting performance on slower devices. • Longer data fetching times on the client can lead to increased energy usage on end-user devices

Slide 15

Slide 15 text

Basic Client Side Rendering import { useEffect, useState } from "react"; export default function Home() { const [data, setData] = useState([]); useEffect(() !=> { fetch('https:!//example.com/data.json') .then((res) !=> res.json()) .then((json) !=> setData(json)); }, []); return (

Data List!

    {data.map((item, index) !=> (
  • {item.name}!
  • ))} !
!
); }

Slide 16

Slide 16 text

Server Side Rendering (SSR) • Improves time to interactive (TTI) since the data is already rendered when page loads • Better SEO as search engines, caa crawl pre-rendered content • Reduces the load on the client-side, improving performance on low-end devices

Slide 17

Slide 17 text

Tooling: Vite (React) • Primary Use Case: Fast, modern development experience for single-page applications (SPAs). • Focus: Vite is a build tool designed to replace Webpack for front-end projects. It provides a highly optimized development environment with lightning-fast hot module replacement (HMR). • Use Case: Ideal for small to medium SPAs or projects where you want full control over the project structure and configuration. • Key Feature: Extremely fast due to its use of native ES modules in development and pre-bundling using esbuild. • Setup: Minimal and unopinionated. It focuses on speed and leaves the choice of additional tools and libraries up to the developer.

Slide 18

Slide 18 text

Tooling: Next.js • Primary Use Case: Production-ready framework for server-rendered and static websites. • Focus: Next.js is a React framework offering opinionated conventions and built-in features like server-side rendering (SSR), static site generation (SSG), and API routes. • Use Case: Best for large-scale projects, content-heavy websites, or applications requiring advanced routing, SSR, SSG, or backend functionality. • Key Feature: Comes with a built-in development server that supports SSR, SSG, and client-side rendering (CSR). • Setup: Opinionated and comprehensive, providing everything out of the box for a complete full-stack solution.

Slide 19

Slide 19 text

Comparison Feature Next.js Vite Ease of Use Very easy; minimal setup needed. Requires manual setup for SSR. Routing Built-in file-based routing. Requires custom routing setup. Production Readiness Built-in optimizations and deployments. Requires manual optimization. Development Speed Fast but slightly slower than Vite. Extremely fast dev server. Customizability Limited to framework conventions. Highly customizable. Community Support Large, active community. Smaller but growing rapidly. Streaming SSR Fully supported out-of-the-box. Supported but needs configuration. Use Case Best for medium to large-scale apps. Best for lightweight/customized apps.

Slide 20

Slide 20 text

App Router Enables React 18 server components, new routing mechanism, new pattern for fetch() and replaces the "older" methods like getServerSideProps

Slide 21

Slide 21 text

Next.js: SSR export async function getServerSideProps() { const res = await fetch('https:!//example.com/data.json'); const content = await res.json(); const props = { props: { data: content } } return props; } export default function Home({ data }) { return (

Data List!

    {data.map((item, index) !=> (
  • {item.name}!
  • ))} !
!
); } getServerSideProps i s a special Next.js function used to fetch data on the server side for each request to the page. App Router is disabled It runs on the server at request time and passes the fetched data as props to the React component. this is passed as a props to the component

Slide 22

Slide 22 text

React 18 (and Next.js): SSR export default async function Home() { const res = await fetch('https:!//example.com/data.json'); const data = await res.json(); return (

Data List!

    {data.map((item, index) !=> (
  • {item.name}!
  • ))} !
!
); } When component is async and it becomes ssr component Does not use browser- specific logic like useState, useE=ect

Slide 23

Slide 23 text

Key features of SSR • Data Fetching at Request Time • Every time a user requests the page, getServerSideProps runs on the server. • This ensures the page always displays the latest data. • SEO Benefits • The page is pre-rendered with the fetched data on the server. • Search engines can index the fully rendered HTML, improving SEO. • Secure Fetching • API calls or sensitive logic (like using API keys) are handled securely on the server.

Slide 24

Slide 24 text

Developer Control over SSR Optimization • SSR gives developers greater control over the application's infrastructure compared to CSR, which offloads rendering entirely to client devices. • CSR: The carbon footprint depends mainly entirely on the user's browser and device.

Slide 25

Slide 25 text

Real-World Considerations • Global Traffic: For international users, SSR can increase the carbon footprint if the server is centralized and far from the user. Using edge servers mitigates this. • High-Load Applications: SSR's carbon footprint increases with traffic, making caching and scaling critical for reducing environmental impact. • Device Impact in CSR: CSR shifts the rendering workload to client devices, which may be inefficient for lower-end or battery-powered devices, leading to higher energy consumption per user.

Slide 26

Slide 26 text

SSR vs CSR • SSR is generally better for reducing the carbon footprint in scenarios where: • The application serves static or semi-dynamic content. • Users rely on low-powered devices or poor internet connections. • Fast, efficient data delivery is critical. • CSR may be more efficient in scenarios where: • The application involves complex interactivity or frequent data updates. • The user base primarily uses high-performance devices. • Client-side caching can significantly reduce repeated server requests. • For sustainability, a hybrid approach (e.g., Static Site Generation or Server- Side Rendering with Hydration) often provides the best balance, leveraging the strengths of both SSR and CSR.

Slide 27

Slide 27 text

Assignment 01

Slide 28

Slide 28 text

Reducing Bundle Size Code Splitting, Image Optimization, Tree Shaking, Reduce Dependencies

Slide 29

Slide 29 text

Key Areas for Optimization 1. Efficient data fetching (SSR and CSR) 2. Reducing bundle size • Code Splitting • Image Optimization • Tree Shaking • Reduce Dependencies 3. Reducing number of renders 4. Monitoring performance using tools

Slide 30

Slide 30 text

Why Bundle Size Matters for Carbon Footprint • Data Transfer Energy Usage: Data transfer requires energy, and smaller bundles mean less data being transferred. This reduces energy usage in: • User Devices: Less processing power needed to parse and render the content. • Data Centers: Reduced bandwidth and cooling energy requirements. • Improved Device Longevity: • Efficient websites cause less strain on end-user devices, extending their lifespan by reducing unnecessary processing and heat generation. • Better User Experience: • Faster load times reduce bounce rates, encouraging sustainable practices like delivering only the necessary resources.

Slide 31

Slide 31 text

Code Splitting

Slide 32

Slide 32 text

Reducing Bundle Size in React • Code Splitting • Image Optimization • Tree Shaking • Reduce Dependencies

Slide 33

Slide 33 text

Default Behaviour • In a typical React application, all components are bundled together into one or more JavaScript files (called bundles) during the build process. • When a user accesses the application, the browser downloads the entire bundle upfront, including components that might not be immediately needed.

Slide 34

Slide 34 text

Behaviour with Lazy Loading • When you use React.lazy to lazy-load a component, its code is excluded from the initial bundle.Instead, the browser only fetches the code for the lazy-loaded component when the component is about to be rendered in the app. • This reduces the initial load time of the application because only the essential components are loaded upfront. • Imagine you have a page with two sections: Home and Settings. • The Home section is displayed by default when the app loads. • The Settings section can be accessed by clicking a button. • With lazy loading, settings is not downloaded by default

Slide 35

Slide 35 text

Benefits of Lazy Loading • Performance: • Reduces the initial download size of the application. • Improves the app's load time, especially for large applications. • Efficiency: • Saves network bandwidth by not loading unused components or pages. => Carbon Footprint • Ideal for applications with rarely-used features or components. • User Experience: • Users interact with the app sooner, as the essential features load quickly. • The "loading..." fallback provides a smooth transition for the lazy-loaded components.

Slide 36

Slide 36 text

!// Home.js export default function Home() { return

Welcome to the Home Page!!

; } !// Settings.js export default function Settings() { return

Settings Page!

; } !// App.js import React, { useState } from "react"; import Home from "./Home"; import Settings from "./Settings"; function App() { const [showSettings, setShowSettings] = useState(false); return (
setShowSettings(!showSettings)}> Toggle Settings ! {showSettings ? : } !
); } export default App; Both Home.js and Settings.js are imported statically at the top of App.js. During the build process, the code for both components is bundled into the initial JavaScript file.

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

!// Home.js export default function Home() { return

Welcome to the Home Page!!

; } !// Settings.js export default function Settings() { return

Settings Page!

; } !// App.js import React, { useState, Suspense } from "react"; !// Lazy load the Settings component const Settings = React.lazy(() !=> import("./Settings")); function App() { const [showSettings, setShowSettings] = useState(false); return (
setShowSettings(!showSettings)}> Toggle Settings ! Loading!!...!
}> {showSettings ? : } ! ! ); } export default App; The Home component is statically imported, so it is included in the initial bundle and available immediately. The Settings component is not included in the initial bundle because it is lazily loaded using React.lazy. While the browser fetches the Settings component, the
Loading...
specified in the Suspense component is displayed.

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Lazy Loading in Next.js • React.lazy is designed for client-side rendering, it does not SSR • Dynamic import for components support CSR and SSR • Automatic route-based code splitting • Lazy loading of images using component

Slide 42

Slide 42 text

Next.js: Dynamic import for components • React.lazy • Designed for client-side rendering only. It does not support server-side rendering (SSR). • If you use React.lazy in a Next.js app that uses SSR, the server will not pre- render the lazy-loaded component, potentially breaking SSR or returning an empty page. • Next.js dynamic • Supports both SSR and client-side rendering (CSR). • You can configure it to enable or disable SSR for a specific component using the ssr option.

Slide 43

Slide 43 text

Next.js dynamic vs React.lazy Feature React.lazy Next.js dynamic Where it can be used Always outside the component. Can be used both inside and outside the component. Supports SSR No, client-side only. Yes, if ssr: true is specified. Conditional Imports Not possible. Possible (e.g., inside a button click handler). Fallback Rendering Requires Suspense for fallback. Customizable fallback via the loading option. Flexibility Limited to static, client-side usage. Highly flexible: supports SSR, CSR, and runtime decisions.

Slide 44

Slide 44 text

import dynamic from "next/dynamic"; import { useState } from "react"; export async function getServerSideProps() { const response = await fetch("https:!//jsonplaceholder.typicode.com/users"); const data = await response.json(); return { props: { customers: data, }, }; } console.log("app loaded"); export default function App({ customers }) { const [showTable, setShowTable] = useState(false); const CustomerTable = dynamic(() !=> import("!../components/CustomerTable"), { ssr: false, !// Disable SSR loading: () !=>

Loading customer data!!...!

, }); return (

Customer Information!

setShowTable((prev) !=> !prev)}> {showTable ? "Hide Customers" : "Show Customers"} ! {showTable !&& } !
); }

Slide 45

Slide 45 text

console.log("Table loaded"); function CustomerTable({ data }) { if (!Array.isArray(data) !|| data.length !!=== 0) { return

No customer data available.!

; } return ( ID! Name! Email! Phone! ! ! {data.map((customer) !=> ( {customer.id}! {customer.name}! {customer.email}! {customer.phone}! ! ))} ! ! ); } export default CustomerTable;

Slide 46

Slide 46 text

!// App.js import React, { useState } from "react"; import dynamic from "next/dynamic"; import Main from "!../components/Main"; export async function getServerSideProps(context) { console.log("SSR"); const showTable = context.query.showtable !!=== "true"; let customers = []; if (showTable) { console.log("Fetch api"); const response = await fetch("https:!//jsonplaceholder.typicode.com/users"); customers = await response.json(); } return { props: { customers, initialShowTable: showTable, }, }; } console.log("Home loaded"); function Home({ customers, initialShowTable }) { const [showTable, setShowTable] = useState(initialShowTable); const CustomerTable = dynamic(() !=> import("!../components/CustomerTable"), { ssr: true, loading: () !=>

Loading customer data!!...!

, }); return (

Customer Information!

{showTable ? : } !
); } export default Home; ssr is true conditional fetching

Slide 47

Slide 47 text

Next.js Routes

Slide 48

Slide 48 text

Next.js: Lazy Loading Routes • Next.js automatically performs route-based code splitting, meaning each page is bundled separately. • When a user navigates to a new route, the corresponding JavaScript for that page is fetched dynamically.

Slide 49

Slide 49 text

Next.js: Routing • Each file in the pages/ directory corresponds to a route. • For example: • pages/index.js → / • pages/about.js → /about • pages/contact.js → /contact • You don’t need a separate router configuration like in React apps with react-router-dom.

Slide 50

Slide 50 text

Example: pages/page1.js export default function Page1() { return (

Welcome to Page 1!

This is the content of Page 1.!

!
); }

Slide 51

Slide 51 text

Best practice: Separate components • Reusability: Components can be reused across multiple pages or other components. • Readability: Smaller components make your code easier to read, maintain, and debug. • Separation of Concerns: Keeps your pages focused on routing and layout while moving reusable logic and presentation to components.

Slide 52

Slide 52 text

pages/page1.js import Message from '!../components/Message'; export default function Page1() { return (

Welcome to Page 1!

!
); }

Slide 53

Slide 53 text

components/Message.js export default function Message({ text }) { return

{text}!

; }

Slide 54

Slide 54 text

App Router SSR and App Router

Slide 55

Slide 55 text

App Router • The App Router in Next.js (introduced in version 13) is a new paradigm for structuring applications using React Server Components (RSC) • It is an alternative to the traditional Pages Router, enabling a more flexible and modular approach to building applications.

Slide 56

Slide 56 text

Key Features of the App Router • React Server Components (RSC) • Allows components to run on the server by default, reducing client-side JavaScript and improving performance. • Components in the App Router are server-rendered unless explicitly marked as client components. • File-Based Routing with Nested Layouts • Introduces nested folder-based routing with support for colocating layouts, pages, and components. • url1/page.js, url2/page.js • Data Fetching at the Component Level • Provides built-in data fetching methods (e.g., fetch) directly in React Server Components.

Slide 57

Slide 57 text

app/posts/page.js import { Suspense } from 'react'; import PostList from './PostList'; export default function PostsPage() { return (

Posts!

Loading posts!!...!}> ! !
); }

Slide 58

Slide 58 text

app/posts/PostList.js async function fetchPosts() { const response = await fetch('https:!//jsonplaceholder.typicode.com/posts'); if (!response.ok) { throw new Error('Failed to fetch posts'); } return response.json(); } export default async function PostList() { const posts = await fetchPosts(); return (
    {posts.slice(0, 5).map(post !=> (
  • {post.title}!

    {post.body}!

    !
  • ))} !
); }

Slide 59

Slide 59 text

SSR on by default • In the Next.js App Router, components are server components by default • sent as HTML to the client • To explicitly mark a component as a client component, you need to add the special directive "use client" at the top of the file. • Client components can use React hooks (e.g., useState, useEffect) and interact with browser APIs (like localStorageor window).

Slide 60

Slide 60 text

Image Optimization

Slide 61

Slide 61 text

Reducing Bundle Size in React • Code Splitting • Image Optimization • Tree Shaking • Reduce Dependencies

Slide 62

Slide 62 text

Image Optimization in React • Next-gen Formats: Use modern formats like WebP, which provide better compression and quality compared to traditional formats like JPEG or PNG. • SVG for Vector Graphics: Use SVG for icons and simple graphics, as they scale well and have smaller file sizes. • Use the loading="lazy" attribute in tags. • Libraries like React Lazy Load or built-in features in frameworks like Next.js (next/image) can simplify this process. • Serve appropriately sized images based on the user's device and screen resolution.

Slide 63

Slide 63 text

Native Lazy Loading • Example • description • Libraries like React Lazy Load or built-in features in frameworks like Next.js (next/image) can simplify this process.

Slide 64

Slide 64 text

Use Responsive Images • Serve appropriately sized images based on the user's device and screen resolution. • Use the srcset attribute for tags to define multiple image sizes. • Tools like sharp (Node.js library) can generate different image sizes during the build process.

Slide 65

Slide 65 text

Common Responsive Design Breakpoints • Extra small devices: max-width: 575px (phones). • Small devices: 576px to 767px (small tablets). • Medium devices: 768px to 991px (larger tablets). • Large devices: 992px to 1199px (laptops). • Extra-large devices: 1200px+ (desktops).

Slide 66

Slide 66 text

HTML5.1: Example Responsive Example Define the width of the image in pixels, so 575w => 575px if viewport width is less than 575px then.. .. the browser assumes that image will take up to 575 CSS pixels of space

Slide 67

Slide 67 text

Scenario 1: Viewport Width = 500px, DPR = 1 • max-width: 575px applies because 500px ≤ 575px. • The browser assumes the image will occupy 575 CSS pixels. • Required resolution = 575px × 1 = 575px. • The browser fetches the 575w image from the srcset.

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

Scenario 1: Viewport Width = 500px, DPR = 2 • max-width: 575px applies because 500px ≤ 575px. • The browser assumes the image will occupy 575 CSS pixels. • Required resolution = 575px × 2 = 1150px. • The browser fetches the 1200w image from the srcset, 992w is too small

Slide 70

Slide 70 text

HTML5.1: Example A beautiful landscape For users who do not scroll to the bottom of the page, images that remain off-screen are never loaded. Pages with many images (e.g., galleries, news articles, or e- commerce listings) benefit the most from lazy loading. Users can start interacting with the page content (text, buttons, links) sooner, as the browser prioritizes visible elements over off-screen images.

Slide 71

Slide 71 text

Optimize with Framework Features • If you're using a React framework like Next.js, take advantage of its built-in component: • Automatically optimizes images. • Generates responsive sizes. • Supports lazy loading and WebP.

Slide 72

Slide 72 text

Next.js: Lazy Loading with • Lazy loading images in Next.js can be efficiently achieved using the component, which is part of Next.js's built-in image optimization features. • The component automatically supports lazy loading by default, ensuring that images are only loaded when they enter the viewport. • This improves page load performance, reduces bandwidth usage, and enhances user experience.

Slide 73

Slide 73 text

Key Features • Lazy Loading by Default: Images are lazily loaded, meaning they are not downloaded by the browser until they are about to be visible on the user's screen. • Automatic Optimization: The component optimizes images on the server side, delivering them in formats like WebP when supported, and resizing images to fit the specified dimensions. • Placeholder Support: You can define a placeholder (blur or empty) to show before the image loads, further enhancing the visual experience. • Images are cached and served for subsequent requests, reducing server workload.

Slide 74

Slide 74 text

WebP • WebP is a modern image format developed by Google that provides superior lossless and lossy compression for images on the web • Designed to reduce image file sizes while maintaining high visual quality, thereby improving website performance by reducing page load times and bandwidth usage

Slide 75

Slide 75 text

WebP: Key Features • WebP images are typically 25–34% smaller than JPEG images and up to 26% smaller than PNG images of comparable quality. • WebP supports transparency (alpha channel) in both lossless and lossy modes, unlike JPEG. • WebP supports animated images, providing a more efficient alternative to formats like GIF. • Most modern browsers (Chrome, Firefox, Edge, and Safari 14+) support WebP. However, fallback mechanisms may be needed for older browsers.

Slide 76

Slide 76 text

Tools • Command-Line Tools: Use ImageMagick or cwebp for powerful, scriptable workflows. • Automated Workflows: Use sharp (Node.js) or Pillow (Python) for integration into development pipelines. • One-Off Conversions: Use GUI tools like XnConvert or online tools like Squoosh for occasional use.

Slide 77

Slide 77 text

Next.js Example import Image from 'next/image'; export default function Example() { return (

Lazy Loading Example!

!
); }

Slide 78

Slide 78 text

Next.js Placeholder

Slide 79

Slide 79 text

Benefits of Using for Lazy Loading • Improved Performance: Unnecessary images are not loaded upfront, reducing initial page load time. • SEO Friendly: The alt attribute ensures accessibility and improves SEO. • Responsive Design: The component works seamlessly with responsive layouts, adapting to different screen sizes. • Out-of-the-box Support: Next.js handles all the complex parts, such as creating optimized image formats, implementing lazy loading, and adding responsive behavior.

Slide 80

Slide 80 text

and Responsive Design • The component can automatically adjust its size to match the parent container, making it fit dynamically within responsive layouts. • Next.js automatically generates multiple versions of the image at different resolutions (e.g., 1x, 2x, etc.) and serves the appropriate one based on the user’s screen resolution. • If the image is rendered on a device with a Retina display, the component ensures a higher-resolution version of the image is used, improving clarity without requiring manual intervention.

Slide 81

Slide 81 text

srcSet with import Image from 'next/image'; export default function ResponsiveImage() { return ( ); } Automatically generate multiple versions of example-image.jpg at various resolutions (e.g., 480w, 1024w, etc.).

Slide 82

Slide 82 text

Next.js generates.. A beautiful landscape

Slide 83

Slide 83 text

Tree Shaking

Slide 84

Slide 84 text

Reducing Bundle Size in React • Code Splitting • Image Optimization • Tree Shaking • Reduce Dependencies

Slide 85

Slide 85 text

Tree Shaking • Tree shaking is a technique used to eliminate dead code—unused parts of the codebase—from the final bundle during the build process. • This optimization reduces the size of the JavaScript bundle that is delivered to the user, which can improve performance by speeding up loading times and reducing resource usage.

Slide 86

Slide 86 text

How Tree Shaking Works • Modern JavaScript tools, like Webpack, Rollup, and Parcel, use static analysis to inspect the dependency graph of the project. • They analyze the import and export statements in ES6 modules to determine which parts of the code are actually used. • Code that is not referenced in the application (i.e., not "reachable" during execution) is considered "dead code" and is removed from the final bundle. • After tree shaking, the resulting code is further minified by tools like Terser or UglifyJS to reduce its size.

Slide 87

Slide 87 text

In React Context • React Components: Tree shaking can help exclude unused React components if they are modularized properly (i.e., exported and imported correctly). • For example, if your project only uses Button from a library but not Card, tree shaking can exclude Card if the library supports tree-shakeable exports. • Utility Functions: Similarly, utility functions or hooks that are imported but not used in the project will be removed during tree shaking.

Slide 88

Slide 88 text

Prerequisites for Tree Shaking • ES Modules (ESM) • Properly Configured Build Tools: • Tools like Webpack or Rollup need to be setup up. Usually on by default. • Library Support • The libraries and dependencies you use must be designed to support tree shaking, which means they need to export individual modules instead of bundling everything into a single export.

Slide 89

Slide 89 text

Example in React • Tree-shakeable import • import debounce from 'lodash/debounce'; • Not tree-shakeable • import _ from 'lodash';

Slide 90

Slide 90 text

Tree shaking in Next.js • Tight Integration with Webpack or Turbopack • Webpack • Mature, well-supported, feature-rich, stable, reliable • Slow build times, complex configuration • Turbopack (The Future of Next.js) • Claims to be 700x faster then Webpack during development for large apps • Tree shaking and other optimizations are faster • Developed by Vercel team who has done Next.js • Simpler Configuration • Future-proof • Early stage!

Slide 91

Slide 91 text

Next.js: Turbopack • In Turbopack, tree-shaking is designed to work automatically with minimal input from the developer • Turbopack optimizes the bundling process by making tree-shaking an integral part of its architecture. • Turbopack performs static analysis on your codebase, focusing on ESModules (ESM). • It identifies and removes code that is not imported or referenced in the dependency graph. • It uses the same principles as Webpack but implements them much faster and more efficiently, thanks to its Rust-based architecture. • Turbopack automatically detects unused code (dead code) and eliminates it from the final bundle. • Turbopack deeply analyzes imports from third-party libraries, ensuring that only the necessary modules are included.

Slide 92

Slide 92 text

What developer needs to know? • Use ESM syntax correctly – do not use CommonJS • Choose Tree-Shakeable libraries • They use ESM and offer module exports • No dynamic imports • if(condition) import ...

Slide 93

Slide 93 text

Dependencies

Slide 94

Slide 94 text

Reducing Bundle Size in React • Code Splitting • Image Optimization • Tree Shaking • Reduce Dependencies

Slide 95

Slide 95 text

Reducing Dependencies • Reducing dependencies refers to minimizing the number and size of external libraries or packages included in your project • Dependencies are third-party modules or libraries installed via npm (or similar package managers) • Each dependency contributes to the final size of your application bundle • A larger bundle can slow down page load times, especially on slower networks or devices.

Slide 96

Slide 96 text

Benefits • Smaller bundle size: Every dependency added to your project increases the JavaScript code that browsers need to download and execute, potentially leading to slower performance. • Reduced attack surface: Fewer dependencies mean fewer opportunities for security vulnerabilities. • Better maintainability: With fewer libraries, there’s less risk of version conflicts and outdated or unsupported code. • Carbon Footprint

Slide 97

Slide 97 text

Strategies • Evaluate whether you really need a specific library. Often, functionality can be implemented natively in JavaScript or with a small utility function. • Example: Instead of adding a library like lodash for a single utility function, use native JavaScript methods like Array.map, Object.assign, etc. • Ensure the libraries you include support tree-shaking, which removes unused code during the build process. • Use smaller, modular libraries • date-fns over moment.js? • Use tools like Webpack Bundle Analyzer or Vite's visualizer plugin to identify large dependencies in your project • Use tools like depcheck or manually check package.json for unused or outdated dependencies and remove them.

Slide 98

Slide 98 text

Next.js and Bundle size Feature Description Automatic Tree-Shaking Removes unused JavaScript from libraries. Code Splitting Splits code by route, reducing initial load. Dynamic Imports Load components or libraries on demand. Optimized Images Reduces image sizes with modern formats. Server-Side Rendering Reduces JavaScript required on the client. Static Site Generation Minimizes client-side computation. Bundle Analyzer Identifies large dependencies. External Dependencies Loads heavy libraries via CDN.

Slide 99

Slide 99 text

Reduce number of renders

Slide 100

Slide 100 text

Key Areas for Optimization 1. Efficient data fetching (SSR and CSR) 2. Reducing bundle size 3. Reduce number of renders - useMemo, useCallback, React.Memo 4. Monitoring performance using tools

Slide 101

Slide 101 text

useMemo and useCallback hooks • In React, useMemo and useCallback are hooks that help optimize performance by preventing unnecessary computations and re-renders • By reducing these inefficiencies, you indirectly lower the carbon footprint of your application by using fewer computational resources (CPU, memory, etc.), which in turn reduces energy consumption.

Slide 102

Slide 102 text

useMemo • useMemo is used to memoize the result of an expensive computation so that it is only recomputed when its dependencies change. • const memoizedValue = useMemo(() !=> computeExpensiveValue(a, b), [a, b]); • For expensive computations like sorting, filtering, or large array transformations. • When a calculated value is repeatedly used in your component but doesn’t change frequently. • Compute and memoize a derived value. It returns a value that is cached until its dependencies change.

Slide 103

Slide 103 text

"use client"; import React, { useState } from "react"; export default function FilteredListWithoutMemo({ items }) { const [search, setSearch] = useState(""); const [random, setRandom] = useState(0); const filteredItems = items.filter((item) !=> { console.log("no useMemo", "filtering!!..."); return item.includes(search); }); console.log("no useMemo", "render"); return (
setRandom(Math.random())}>{random}!
setSearch(e.target.value)} placeholder="Search items" !/>
    {filteredItems.map((item, index) !=> (
  • {item}!
  • ))} !
!
); } This is run even if button is clicked

Slide 104

Slide 104 text

"use client"; import React, { useState, useMemo } from "react"; export default function FilteredList({ items }) { const [search, setSearch] = useState(""); const [random, setRandom] = useState(0); !// Start with a stable value const filteredItems = useMemo(() !=> { console.log("useMemo", "filtering"); return items.filter((item) !=> item.includes(search)); }, [items, search]); console.log("useMemo", "render"); return (
setRandom(Math.random())}>{random}!
setSearch(e.target.value)} placeholder="Search items" !/>
    {filteredItems.map((item, index) !=> (
  • {item}!
  • ))} !
!
); } Only run if items are search is changed. If button is pressed it will us cached version of filteredItems

Slide 105

Slide 105 text

useCallback • useCallback is a React Hook that memoizes a function so that it does not get re-created on every render unless its dependencies change. • useMemo memoizes a result of computation • This helps optimize performance, especially in scenarios where functions are passed as props to child components or used in useEffect. • To prevent that a new function instance is created every time the component re-renders.

Slide 106

Slide 106 text

"use client"; import React, { useState, useCallback } from "react"; export default function useCallback1() { const [count, setCount] = useState(0); const increment1 = useCallback(() !=> { setCount((prev) !=> prev + 1); }, []); const increment2 = () !=> { setCount((prev) !=> prev + 1); }; return (
Increment Count - useCallback! Increment Count - NO useCallback!

Count: {count}!

!
); } increment1 – created only once for r— renders. increment2 - created on every re- render

Slide 107

Slide 107 text

"use client"; import React, { useState, useCallback, useRef } from "react"; export default function useCallback1() { const [count, setCount] = useState(0); const increment1 = useCallback(() !=> { setCount((prev) !=> prev + 1); }, []); const increment2 = () !=> { setCount((prev) !=> prev + 1); }; !// Use refs to store the previous references const previousIncrement1Ref = useRef(increment1); const previousIncrement2Ref = useRef(increment2); !// Compare the previous and current references console.log( "increment1:", previousIncrement1Ref.current !!=== increment1 ); console.log( "increment2:", previousIncrement2Ref.current !!=== increment2 ); !// Update the refs for the next render previousIncrement1Ref.current = increment1; previousIncrement2Ref.current = increment2; console.log("LargeComponent rendered"); return (
Increment Count - useCallback! Increment Count - NO useCallback!

Count: {count}!

!
); } increment1 – created only once for r— renders. increment2 - created on every re- render

Slide 108

Slide 108 text

Higher-Order Component (HOC) in React • A Higher-Order Component (HOC) is a function that takes a component as an input and returns a new component. It is a pattern used in React to reuse component logic. • HOCs are not a feature of React itself but a design pattern that emerges from the compositional nature of React components. • const EnhancedComponent = HigherOrderComponent(BaseComponent);

Slide 109

Slide 109 text

import React from "react"; !// Higher-Order Component function withLogging(WrappedComponent) { return function EnhancedComponent(props) { console.log("Props:", props); return ; }; } !// Base Component function MyComponent({ name }) { return

Hello, {name}!!

; } !// Enhanced Component const MyComponentWithLogging = withLogging(MyComponent); export default function App() { return ; }

Slide 110

Slide 110 text

React.memo? • React.memo is a higher-order component (HOC) in React that memoizes a functional component. • It prevents the component from re-rendering unnecessarily by skipping renders when its props haven’t changed. • By default, React re-renders child components whenever a parent re- renders, even if the child's props are the same. • When you wrap a functional component with React.memo, React shallowly compares the component's new and previous props. • const MemoizedComponent = React.memo(MyComponent);

Slide 111

Slide 111 text

"use client"; import React, { useState, memo } from "react"; function Child({ count }) { console.log("Child component rendered"); return

Child Count: {count}!

; } const ChildComponentMemorizied = React.memo(Child); function Parent() { const [count, setCount] = useState(0); const [otherState, setOtherState] = useState(0); console.log("Parent component rendered"); return (
setCount((prev) !=> prev + 1)}> Increment Count ! setOtherState((prev) !=> prev + 1)}> Update Other State ! !
); } When other state changes, child is not re-rendered!

Slide 112

Slide 112 text

React.memo and useCallback? • useCallback comes into play when you pass functions as props to a child component wrapped with React.memo • Even if the logic of the function remains the same, React creates a new function reference on every render • This causes React.memo to treat the function prop as "changed," resulting in unnecessary re-renders of the child component.

Slide 113

Slide 113 text

Child function Child({ count, onClick }) { console.log("Child component rendered"); return (

Child Count: {count}!

Say Hello From Child! !
); } const ChildComponentMemorized = React.memo(Child);

Slide 114

Slide 114 text

function Parent() { const [count, setCount] = useState(0); const [otherState, setOtherState] = useState(0); !// Function defined inline (re-created on every render) const handleClick = () !=> { alert("Hello from child component"); }; console.log("Parent component rendered"); return (
setCount((prev) !=> prev + 1)}> Increment Count ! setOtherState((prev) !=> prev + 1)}> Update Other State {otherState} ! !
); } New function instance in every render Passing new props on every render, child re-renders

Slide 115

Slide 115 text

function Parent() { const [count, setCount] = useState(0); const [otherState, setOtherState] = useState(0); !// Function defined inline (re-created on every render) const handleClick = useCallback(() !=> { alert("Hello from child component"); }, []); console.log("Parent component rendered"); return (
setCount((prev) !=> prev + 1)}> Increment Count ! setOtherState((prev) !=> prev + 1)}> Update Other State {otherState} ! !
); } Same function instance in every render Passing same props on every render, child does not re- render

Slide 116

Slide 116 text

Tools for Analysing

Slide 117

Slide 117 text

Key Areas for Optimization 1. Efficient data fetching (SSR and CSR) 2. Reducing bundle size 3. Reduce number of renders 4. Monitoring performance using tools

Slide 118

Slide 118 text

Tools for Analysis • Chrome DevTools • Lighthouse for performance metrics • Network tab for analyzing requests • React DevTools • Identifying unnecessary re-renders • Next.js Bundle Analyzer • Monitoring and optimizing bundle size

Slide 119

Slide 119 text

Chrome Devtools: Lighthouse

Slide 120

Slide 120 text

Report: Performance metrics • Speed Index - How quickly content is visually displayed during loading • First Contentful Paint (FCP) • Largest Contentful Paint (LCP) • Time to Interactive (TTI) • Cumulative Layout Shift (CLS) • Total Blocking Time (TBT) • Time to Interactive (TTI)

Slide 121

Slide 121 text

Energy Impact • Less Processing Power • Faster-loading pages reduce CPU and GPU usage, minimizing energy consumption on both the client device (user’s browser) and the server. • Fewer Resources Downloaded • Optimizing images, scripts, and other assets reduces bandwidth usage, lowering energy demands in data centers and network infrastructure. • Reduced Idle Time • Efficient performance avoids unnecessary delays, allowing devices to return to low-power or idle states sooner.

Slide 122

Slide 122 text

Performance Metrics Metric Weight Largest Contentful Paint 25 % Total Blocking Time 25 % Cumulative Layout Shift 15 % Speed Index 15 % First Contentful Paint 10 % Time to Interactive 10 %

Slide 123

Slide 123 text

Network Tab

Slide 124

Slide 124 text

Network Tab • Filter requests • Use filters like XHR (AJAX requests), JS, CSS, Img, etc., to narrow down specific resources. • Columns • Name, status, type, size, time, waterfall • Throttling • Simulate slower connections (e.g., "Fast 3G" or "Offline") from the dropdown menu to test performance under varying conditions.

Slide 125

Slide 125 text

React Dev Tools • React DevTools is a browser extension (available for Chrome and Firefox) that allows you to inspect and debug the component tree of a React application • It provides insights into the structure of your app, state, props, and component performance.

Slide 126

Slide 126 text

Components tab • Inspect Component Tree • View Props and State • Edit Props or State (For Debugging)

Slide 127

Slide 127 text

Profiler tab • Record Renders • Click "Start profiling" to record interactions and renders • It shows a timeline of rendering activity • Analyze Performance • Identify which components take the most time to render. • Look for unnecessary re-renders (components rendering when they don’t need to). • Optimize Rendering • Use the Profiler data to optimize expensive components

Slide 128

Slide 128 text

Next.js Bundle Analyzer • The Next.js Bundle Analyzer is a tool that helps you visualize the size and structure of your Next.js application’s JavaScript bundles. • Generates an interactive treemap of your bundles, allowing you to identify large or unnecessary dependencies and optimize your app's performance

Slide 129

Slide 129 text

Why Use Next.js Bundle Analyzer? • Identify Large Dependencies • Find out which libraries or modules are contributing the most to your bundle size. • Optimize Performance: • Reducing the bundle size improves loading times, especially for users on slower networks or devices. • Debug Code Splitting: • Visualize how your code is split into chunks and ensure unnecessary code is not loaded upfront. • Detect Unused Code: • Spot unused or redundant code that can be removed to reduce bundle size.

Slide 130

Slide 130 text

Install: npm install @next/bundle-analyzer • Configure next.config.mjs import withBundleAnalyzer from "@next/bundle-analyzer"; const withAnalyzer = withBundleAnalyzer({ enabled: process.env.ANALYZE !!=== "true", !// Enable analyzer only when ANALYZE=true openAnalyzer: true, !// Automatically open the report in the browser }); const nextConfig = { reactStrictMode: false, !// Other Next.js configurations }; export default withAnalyzer(nextConfig);

Slide 131

Slide 131 text

Add script and open reports • "analyze": "ANALYZE=true npm run build"

Slide 132

Slide 132 text

nodejs.html • This report shows the JavaScript bundle used in the Node.js runtime during server-side rendering (SSR). • It includes code required to generate server-rendered pages or handle API routes. • If your app heavily relies on SSR or has API routes, this report helps you optimize server-side code and dependencies. • Useful for identifying large server-only libraries that could slow down server-side rendering.

Slide 133

Slide 133 text

Edge Computing • Node.js server can be in Australia – when doing fetch api takes time if doing it from Finland • With edge computing you can spread small servers all around the world so that you can access them faster • Now if doing the connection, you connect to a small server in Finland instead of Australia • Edge is fast, but it has limitations • they can’t handle big tasks like running a full database or doing heavy calculations. • You redirect users, authentication

Slide 134

Slide 134 text

edge.html • This report shows the JavaScript bundle used when deploying your app to Edge environments, such as Vercel Edge Functions or other Content Delivery Networks (CDNs). • Edge functions are often used for running server-side logic closer to the user, improving performance. • If you’re deploying your app to an edge network (e.g., Vercel, Cloudflare Workers), this report helps you identify and optimize the code that runs in the Edge runtime. • Ensure minimal and efficient dependencies to meet size and execution time limits often imposed by edge environments.

Slide 135

Slide 135 text

client.html • This report shows the JavaScript bundle sent to the client-side (browser). • It includes all code required for hydration (converting server- rendered HTML into a fully interactive React app) and any other client-side logic. • This is the most critical report for optimizing frontend performance because it affects the loading speed and interactivity of your app. • Large bundles here can cause slow load times, especially on slower networks.

Slide 136

Slide 136 text

Conclusion • nodejs.html: Represents the server-side code responsible for generating pages or API responses on the Node.js backend. • edge.html: Represents server-side code optimized for edge networks, where server-side logic is executed closer to the end user. • client.html: Represents client-side JavaScript that runs in the browser, responsible for interactivity, hydration, and user experience.

Slide 137

Slide 137 text

No content