1|Page
FRONTEND APP DEVELOPMENT WITH REACT.JS
CODE: SWDFA501
Develop React.js application
Objective: ReactJS environment is properly prepared based on application requirements.
Preparation of React js environment
Definition of key concepts:
ReactJS – A JavaScript library for building user interfaces, primarily for single-page applications.
It allows developers to create reusable UI components and efficiently update and render them.
Component – A self-contained, reusable piece of UI in a React application. Components can be
class-based or functional and manage their own state and behavior.
JSX (JavaScript XML) – A syntax extension for JavaScript that allows writing HTML-like code
within JavaScript. JSX makes it easier to describe UI structures and is transformed into standard
JavaScript by React’s build process.
Props (Properties) – Read-only attributes passed from a parent component to a child
component in React. Props allow components to receive dynamic data and behave accordingly.
State – A built-in object that allows React components to store and manage dynamic data. State
changes trigger re-renders, enabling interactive UI updates.
Lifecycle Methods – Special methods in class components that run at different stages of a
component’s lifecycle (e.g., componentDidMount, componentDidUpdate,
componentWillUnmount). They help manage side effects like data fetching and cleanup.
Hooks – Functions that allow functional components to use state and other React features
without needing class components. Common hooks include useState, useEffect, and useContext.
Virtual DOM – A lightweight copy of the actual DOM that React uses to optimize rendering.
React updates the Virtual DOM first and efficiently determines the minimal changes needed
before updating the real DOM.
React Router – A library for handling navigation in React applications. It enables developers to
implement client-side routing, allowing different views to be rendered based on the URL
without a full page reload.
Redux – A state management library for JavaScript applications, commonly used with React. It
provides a centralized store for managing global application state in a predictable way using
actions and reducers.
By Chris H (Lycee APADE) Page 1
2|Page
Introduction
Uses of react (Benefits or Advantages)
ReactJS is widely used for building dynamic and interactive user interfaces, particularly in
single-page applications (SPAs) and modern web applications. Here’s how and why React is
used:
Building Reusable UI Components
React allows developers to break down complex UIs into smaller, reusable
components. Components can be nested, managed independently, and shared across
different parts of the application.
Managing Application State Efficiently
React provides state management using the useState hook or external libraries like
Redux and Context API. This ensures smooth UI updates without unnecessary re-
renders.
Creating Interactive and Dynamic UIs
React efficiently updates only the necessary parts of the DOM using its Virtual DOM.
User interactions (like clicking a button, typing, or fetching data) trigger state updates
and render only the changed components, improving performance.
Handling Navigation with React Router
React Router enables client-side routing, allowing users to navigate different pages
within a single-page application without a full-page reload. This improves speed and
provides a seamless experience.
Supporting Modern Development with Hooks
Hooks (useState, useEffect, useContext, etc.) allow developers to use state and
lifecycle features in functional components, simplifying development.
Enabling Server-Side Rendering (SSR) & Static Site Generation (SSG)
Frameworks like Next.js (built on React) enable SSR and SSG, improving SEO and
performance for React apps.
Cross-Platform Development
React powers mobile apps using React Native, enabling cross-platform development
for iOS and Android with a shared codebase.
By Chris H (Lycee APADE) Page 2
3|Page
Seamless Integration with Other Technologies
React can be integrated with REST APIs, GraphQL, WebSockets, and other back-end
services. It also works well with third-party libraries and tools like Material-UI, Tailwind
CSS, and D3.js.
Features
ReactJS is a powerful JavaScript library designed to create efficient and scalable user
interfaces. Here are its core features:
Component-Based Architecture
React applications are built using components, which are reusable, self-
contained pieces of UI. Components can be functional (stateless) or class-
based (stateful). Encourages modular development and reusability.
Virtual DOM for Faster Performance
React maintains a Virtual DOM, a lightweight copy of the real DOM.When state
changes, React updates the Virtual DOM first, identifies minimal changes, and
updates only the necessary parts of the actual DOM. This improves
performance and efficiency.
JSX (JavaScript XML)
JSX allows developers to write HTML-like syntax inside JavaScript. It makes UI
code more readable and declarative.
Example:
One-Way Data Binding
React follows a unidirectional data flow, making data easier to track and
debug. Props (read-only) allow data to flow from parent to child components.
React Hooks
By Chris H (Lycee APADE) Page 3
4|Page
Hooks like useState, useEffect, and useContext allow functional components to
manage state and handle side effects. Eliminates the need for class
components in most cases.
Component Lifecycle Methods
Class components have lifecycle methods like:
componentDidMount() – Runs after component is added to the DOM.
componentDidUpdate() – Runs when state/props update.
componentWillUnmount() – Cleans up before component removal.
Functional components use the useEffect hook for lifecycle-like behavior.
State Management
React manages component-specific state using useState. For global state
management, tools like Redux, Context API, Recoil, and Zustand are commonly
used.
React Router for Navigation
React Router enables client-side routing, allowing multi-page experiences in SPAs
without full-page reloads.
Server-Side Rendering (SSR) with Next.js
React can be used with Next.js for faster page loads and SEO optimization. Supports
SSR, Static Site Generation (SSG), and Incremental Static Regeneration (ISR).
Cross-Platform Development with React Native
React is used to build mobile applications using React Native for both iOS and
Android.
Large Ecosystem & Community Support
React has a vast ecosystem with libraries like Material-UI, Tailwind CSS, Ant Design,
and state management tools like Redux and Recoil. Strong community support and
frequent updates.
Easy Debugging with Developer Tools
React Developer Tools extension helps debug components, state, and props easily.
By Chris H (Lycee APADE) Page 4
5|Page
Installation of NodeJS and Node Package Manager (NPM)
Windows Installation
1. Download Node.js
Visit the official Node.js website.
Download the LTS (Long-Term Support) version (recommended for stability).
2. Run the Installer
Open the downloaded .msi file.
Follow the installation wizard steps.
Select "Add to PATH" during installation.
3. Verify Installation
Open Command Prompt (cmd) or PowerShell and type:
If installed correctly, it will display the Node.js version.
Check NPM version:
4. Update NPM (Optional but Recommended)
Creating React Application
Method 1: Using Create React App (CRA)
1. Install Create React App (Optional, if not globally installed)
npm install -g create-react-app
2. Create a New React App
By Chris H (Lycee APADE) Page 5
6|Page
npx create-react-app my-app
npx runs the latest version of Create React App without installing it globally.
Replace my-app with your project name.
3. Navigate to the Project Directory
cd my-app
4. Start the Development Server
npm start
Opens http://localhost:3000/ in your browser with a running React app.
Method 2: Using Vite (Faster and Lightweight Alternative)
1. Create a React App with Vite
npm create vite@latest my-app --template react
For React with TypeScript, use:
npm create vite@latest my-app --template react-ts
2. Navigate to the Project Directory
cd my-app
3. Install Dependencies
npm install
4. Start the Development Server
npm run dev
Opens http://localhost:5173/ (default Vite port).
Project Structure (For Both Methods)
my-app/
│── node_modules/ # Installed dependencies
By Chris H (Lycee APADE) Page 6
7|Page
│── public/ # Static assets (index.html, favicon, etc.)
│── src/ # React components & logic
│ ├── App.js # Main component
│ ├── index.js # Root file rendering App.js
│── package.json # Project metadata & dependencies
│── .gitignore # Files to ignore in Git
│── README.md # Project documentation
Running and Managing the React App
Start the Development Server
npm start # (for CRA)
npm run dev # (for Vite)
Build for Production
npm run build
Install Additional Packages
npm install package-name
Example: Install React Router
npm install react-router-dom
Explore React project structure
By Chris H (Lycee APADE) Page 7
8|Page
By Chris H (Lycee APADE) Page 8
9|Page
Installation of additional React tools and libraries (React Developer
tools)
1. Install React Developer Tools (React DevTools)
React Developer Tools is a browser extension that helps debug React
applications.
For Chrome
Go to the React Developer Tools Chrome Extension
Click "Add to Chrome" → "Add Extension".
By Chris H (Lycee APADE) Page 9
10 | P a g e
Once installed, open Chrome DevTools (F12 or Ctrl + Shift + I) and find
the React tab.
For Firefox
Go to the React Developer Tools Firefox Add-on
Click "Add to Firefox" → "Add".
Open Firefox DevTools (F12 or Ctrl + Shift + I) and find the React tab.
2. Install Additional Useful React Libraries
React Router (For Navigation)
npm install react-router-dom
or
yarn add react-router-dom
Use this for client-side routing.
Redux Toolkit (For State Management)
npm install @reduxjs/toolkit react-redux
or
yarn add @reduxjs/toolkit react-redux
Redux is useful for managing global state efficiently.
Styled Components (For Styling)
npm install styled-components
or
yarn add styled-components
A powerful CSS-in-JS library.
By Chris H (Lycee APADE) Page 10
11 | P a g e
Axios (For API Requests)
npm install axios
or
yarn add axios
A promise-based HTTP client for fetching data.
React Icons (For Icons)
npm install react-icons
or
yarn add react-icons
Includes various icon packs like FontAwesome, Material Icons, and more.
React Testing Library (For Testing)
npm install @testing-library/react jest-dom
or
yarn add @testing-library/react jest-dom
For writing and running tests.
Objective: React basics are correctly applied based on application requirements.
By Chris H (Lycee APADE) Page 11
12 | P a g e
Applying React basics
a. React Components
Concept:
React components are the building blocks of a React application. They help in creating reusable
UI elements. Components can be functional or class-based.
Application:
Functional Components:
Class Components: (older approach)
Components can be nested inside each other, allowing for modular code.
b. JSX (JavaScript XML)
Concept:
JSX is a syntax extension for JavaScript that allows writing HTML-like code inside JavaScript
files. It makes UI easier to write and read.
Application:
JSX allows embedding JavaScript expressions using {}:
By Chris H (Lycee APADE) Page 12
13 | P a g e
Must return a single parent element:
Use className instead of class for CSS classes:
c. Props (Properties)
Concept:
Props are used to pass data between components, making them dynamic and reusable.
Application:
Props are read-only and passed like HTML attributes:
By Chris H (Lycee APADE) Page 13
14 | P a g e
By Chris H (Lycee APADE) Page 14
15 | P a g e
d. Lifecycle Methods
Concept:
Lifecycle methods control what happens at different stages of a component’s life: Mounting,
Updating, and Unmounting.
Application:
Used in class components (functional components use hooks like useEffect).
Mounting (Component Creation):
Updating (When State or Props Change):
Unmounting (Component Removal):
By Chris H (Lycee APADE) Page 15
16 | P a g e
functional components, lifecycle behavior is managed using the useEffect hook:
By Chris H (Lycee APADE) Page 16
17 | P a g e
Objective: UI navigation is correctly applied based on application requirements
Applying UI navigation
By Chris H (Lycee APADE) Page 17
18 | P a g e
By Chris H (Lycee APADE) Page 18
19 | P a g e
By Chris H (Lycee APADE) Page 19
20 | P a g e
By Chris H (Lycee APADE) Page 20
21 | P a g e
By Chris H (Lycee APADE) Page 21
22 | P a g e
By Chris H (Lycee APADE) Page 22
23 | P a g e
Objective: React hooks are correctly applied based on components structure
Applying React hooks
Identifying hooks
React Hooks allow functional components to use state and lifecycle features that were
previously only available in class components.
By Chris H (Lycee APADE) Page 23
24 | P a g e
By Chris H (Lycee APADE) Page 24
25 | P a g e
By Chris H (Lycee APADE) Page 25
26 | P a g e
By Chris H (Lycee APADE) Page 26
27 | P a g e
By Chris H (Lycee APADE) Page 27
28 | P a g e
By Chris H (Lycee APADE) Page 28
29 | P a g e
Hook selection and combination
Common React Hooks and Their Use Cases
1. State & Side Effects
useState → For managing component state
useReducer → When state logic is complex or involves multiple sub-values
useEffect → For handling side effects (e.g., API calls, event listeners)
2. Performance Optimization
useMemo → Memoizes expensive computations
useCallback → Memoizes functions to prevent unnecessary re-renders
useRef → Keeps a persistent reference without triggering re-renders
3. Context & Global State
useContext → Accesses values from React Context
useReducer + useContext → Manages global state with reducers
useSyncExternalStore → For external state synchronization
4. Refs & Lifecycle Control
useRef → Persistent value storage without re-renders
useImperativeHandle → Exposes imperative methods for a ref
useLayoutEffect → Runs synchronously after DOM mutations
By Chris H (Lycee APADE) Page 29
30 | P a g e
Combining Hooks for Effective Usage
Here are some best practices for combining multiple hooks efficiently:
1. Manage Complex State with useReducer + useContext
Instead of using multiple useState hooks, use useReducer inside a Context Provider.
By Chris H (Lycee APADE) Page 30
31 | P a g e
2. Optimize Expensive Computations with useMemo + useCallback
Use useMemo to cache values and useCallback to cache function references.
Example:
By Chris H (Lycee APADE) Page 31
32 | P a g e
3. Fetch Data Efficiently with useEffect + useRef
Use useRef to avoid unnecessary re-renders when tracking if a component is mounted.
Example:
By Chris H (Lycee APADE) Page 32
33 | P a g e
4. Custom Hooks for Code Reusability
When multiple components share the same logic, extract it into a custom hook.
Example:
By Chris H (Lycee APADE) Page 33
34 | P a g e
Optimizing Performance
Optimizing performance in React with hooks involves preventing unnecessary re-renders,
reducing computations, and managing state efficiently. Below are key strategies and best
practices to optimize performance in React with hooks.
1. Prevent Unnecessary Re-renders with useMemo
Use useMemo to cache expensive computations and prevent recalculations on every render.
Example:
By Chris H (Lycee APADE) Page 34
35 | P a g e
2. Optimize Function References with useCallback
useCallback ensures that a function reference remains the same across renders,
preventing unnecessary child re-renders.
Example:
By Chris H (Lycee APADE) Page 35
36 | P a g e
3. Avoid Unnecessary Re-renders with React.memo
React.memo prevents a functional component from re-rendering if its props haven’t changed.
Example:
By Chris H (Lycee APADE) Page 36
37 | P a g e
4. Avoid Unnecessary State Updates with useRef
useRef allows you to persist values without triggering re-renders.
Example:
By Chris H (Lycee APADE) Page 37
38 | P a g e
5. Use useReducer Instead of Multiple useState Calls
For managing complex state, useReducer improves performance by consolidating state
logic.
Example:
By Chris H (Lycee APADE) Page 38
39 | P a g e
Handling Complex State Logic
Handling complex state logic in React requires structured approaches to manage state efficiently
and prevent unnecessary re-renders. Below are strategies and best practices for managing
complex state in React.
By Chris H (Lycee APADE) Page 39
40 | P a g e
Managing Global State
Example:
By Chris H (Lycee APADE) Page 40
41 | P a g e
Example:
By Chris H (Lycee APADE) Page 41
42 | P a g e
By Chris H (Lycee APADE) Page 42
43 | P a g e
By Chris H (Lycee APADE) Page 43
44 | P a g e
By Chris H (Lycee APADE) Page 44
45 | P a g e
By Chris H (Lycee APADE) Page 45
46 | P a g e
Objective: Events handling are properly implemented based on best practices
Implementation of Events handling
Description of Events
In React.js, events are handled similarly to regular JavaScript events but with some differences.
Here are the key points:
By Chris H (Lycee APADE) Page 46
47 | P a g e
a. Event Handling in React
React uses camelCase for event names instead of lowercase. For example:
<button onClick={handleClick}>Click Me</button>
Instead of onclick, React uses onClick.
b. Passing Event Handlers
Event handlers are passed as functions, not strings (as in vanilla JavaScript).
function handleClick() {
alert("Button Clicked!");
}
<button onClick={handleClick}>Click Me</button>
c. Synthetic Events
React uses SyntheticEvent, which is a wrapper around the browser's native event system.
function handleClick(event) {
console.log(event); // SyntheticEvent object
}
d. Passing Arguments to Event Handlers
If you need to pass arguments to an event handler, use an arrow function:
<button onClick={() => handleClick("Hello")}>Click Me</button>
function handleClick(message) {
alert(message);
}
e. Prevent Default Behavior
To prevent default behavior (e.g., preventing form submission):
function handleSubmit(event) {
event.preventDefault();
console.log("Form submitted");
}
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
By Chris H (Lycee APADE) Page 47
48 | P a g e
f. Event Bubbling and Stopping Propagation
To stop event propagation:
function handleClick(event) {
event.stopPropagation();
}
g. Common React Events
React supports many event types, such as:
Mouse Events: onClick, onDoubleClick, onMouseEnter, onMouseLeave
Keyboard Events: onKeyDown, onKeyPress, onKeyUp
Form Events: onChange, onSubmit, onFocus, onBlur
Clipboard Events: onCopy, onCut, onPaste
Touch Events: onTouchStart, onTouchMove, onTouchEnd
h. Inline Event Handlers
You can also define event handlers inline:
<button onClick={() => alert("Button Clicked!")}>Click Me</button>
Debouncing and Throttling Events
Debouncing and throttling are techniques used to optimize performance by controlling how
frequently a function executes, especially in event-driven scenarios like user input, scrolling, or
resizing.
Debouncing
Debouncing ensures a function runs only after a certain delay has passed since the last
invocation.
Useful for handling search inputs, window resizing, and API calls.
Prevents unnecessary function execution for every keystroke or event.
Example: Debounced Search Input
import React, { useState, useEffect } from "react";
By Chris H (Lycee APADE) Page 48
49 | P a g e
function DebouncedSearch() {
const [query, setQuery] = useState("");
const [debouncedQuery, setDebouncedQuery] = useState("");
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedQuery(query);
}, 500); // Delay of 500ms
return () => clearTimeout(handler); // Cleanup previous timeout
}, [query]);
return (
<div>
<input
type="text"
placeholder="Search..."
onChange={(e) => setQuery(e.target.value)}
/>
<p>Searching for: {debouncedQuery}</p>
</div>
);
}
export default DebouncedSearch;
How it works:
Each keystroke resets the setTimeout.
The function only executes if the user stops typing for 500ms.
Throttling
Throttling ensures a function runs at most once in a specified time interval, even if triggered
multiple times.
Useful for optimizing performance in events like scrolling, resizing, or rapid button
clicks.
Example: Throttled Scroll Event
Using lodash's throttle function:
By Chris H (Lycee APADE) Page 49
50 | P a g e
import React, { useEffect, useState } from "react";
import { throttle } from "lodash";
function ThrottledScroll() {
const [scrollY, setScrollY] = useState(window.scrollY);
useEffect(() => {
const handleScroll = throttle(() => {
setScrollY(window.scrollY);
}, 1000); // Run at most once per second
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return <h1>Scroll Position: {scrollY}px</h1>;
}
export default ThrottledScroll;
�How it works:
The handleScroll function is called at most once per second (1000ms).
Even if the user keeps scrolling, the function won’t execute more than once per interval.
Using Controlled Components
In React, a controlled component is an input element (like <input>, <textarea>, <select>)
whose value is controlled by the React state rather than the DOM.
Why use Controlled Components?
Single Source of Truth: The React state becomes the "single source of truth" for the form data,
making it easier to handle and validate.
State Synchronization: The form elements are always in sync with the component's state.
Improved Control: You can manipulate the form data and manage user input effectively.
By Chris H (Lycee APADE) Page 50
51 | P a g e
1) Basic Example: Controlled Text Input
In a controlled component, the form element’s value is set by React state, and any changes are
reflected in the state.
import React, { useState } from 'react';
function ControlledForm() {
const [name, setName] = useState("");
const handleChange = (event) => {
setName(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
alert('Name submitted: ' + name);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={name}
onChange={handleChange}
/>
</label>
<button type="submit">Submit</button>
</form>
);
}
export default ControlledForm;
How it works:
The value of the <input> is controlled by the name state (value={name}).
The onChange event updates the name state when the user types.
2. Controlled Checkbox
Checkboxes work the same way as text inputs but with a boolean state.
jsx
CopyEdit
import React, { useState } from 'react';
function ControlledCheckbox() {
const [isChecked, setIsChecked] = useState(false);
By Chris H (Lycee APADE) Page 51
52 | P a g e
const handleChange = () => {
setIsChecked(!isChecked);
};
return (
<label>
<input
type="checkbox"
checked={isChecked}
onChange={handleChange}
/>
Check me!
</label>
);
}
export default ControlledCheckbox;
How it works:
The checked attribute is controlled by the isChecked state.
The onChange event updates the state when the checkbox is toggled.
3. Controlled Textarea
Similar to text inputs, a <textarea> can also be controlled by React state.
jsx
CopyEdit
import React, { useState } from 'react';
function ControlledTextarea() {
const [text, setText] = useState("");
const handleChange = (event) => {
setText(event.target.value);
};
return (
<div>
<textarea
value={text}
onChange={handleChange}
rows="4"
cols="50"
/>
<p>Content: {text}</p>
</div>
);
}
export default ControlledTextarea;
By Chris H (Lycee APADE) Page 52
53 | P a g e
How it works:
The value of the <textarea> is controlled by the text state.
Changes are reflected in the text state when the user types.
4. Controlled Select Dropdown
Here’s how to control a <select> dropdown element:
import React, { useState } from 'react';
function ControlledSelect() {
const [selectedOption, setSelectedOption] = useState("");
const handleChange = (event) => {
setSelectedOption(event.target.value);
};
return (
<div>
<label>
Select a color:
<select value={selectedOption} onChange={handleChange}>
<option value="">--Select--</option>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
</label>
<p>Selected color: {selectedOption}</p>
</div>
);
}
export default ControlledSelect;
How it works:
The value of the <select> is controlled by the selectedOption state.
The onChange event updates the selected value in the state.
5. Benefits of Controlled Components
Validation: Easily validate the form fields before submission.
Reset State: Can programmatically reset form values by modifying state.
Real-time Updates: Can manipulate data immediately as the user types or selects.
Dynamic Behavior: React can dynamically update the form (e.g., enabling/disabling submit
button based on user input).
By Chris H (Lycee APADE) Page 53
54 | P a g e
6. Example: Complete Form with Controlled Inputs
import React, { useState } from 'react';
function ContactForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
message: "",
});
const handleChange = (event) => {
const { name, value } = event.target;
setFormData({
...formData,
[name]: value,
});
};
const handleSubmit = (event) => {
event.preventDefault();
alert('Form submitted: ' + JSON.stringify(formData));
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
</label>
<br />
<label>
Email:
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
</label>
<br />
<label>
Message:
<textarea
By Chris H (Lycee APADE) Page 54
55 | P a g e
name="message"
value={formData.message}
onChange={handleChange}
/>
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}
export default ContactForm;
Conclusion
Controlled components make it easier to manage form data in React and provide better control
over input handling and validation.
They ensure that the React state is the single source of truth for the form inputs.
Passing Arguments to Event Handlers
In React, event handlers usually receive the event object by default. However, if you want to
pass additional arguments (besides the event object), you can do so using a few common
approaches:
1. Using an Arrow Function in the onClick or onChange
You can pass arguments to an event handler using an arrow function. This allows
you to bind arguments to the function while maintaining the event object.
Example: Passing a String to onClick
jsx
CopyEdit
import React from 'react';
function Button() {
const handleClick = (message) => {
alert(message);
};
return (
<button onClick={() => handleClick('Hello from Button!')}>
Click Me
</button>
);
}
By Chris H (Lycee APADE) Page 55
56 | P a g e
export default Button;
How it works:
The arrow function () => handleClick('Hello from Button!') allows
you to pass the string 'Hello from Button!' to the handleClick function.
When the button is clicked, it triggers the alert with the provided message.
2. Using bind() to Pass Arguments
Another method is using bind(). This is commonly used in class components or
when you need to bind the function to a specific context (e.g., this).
Example: Using bind() to Pass Arguments
import React from 'react';
class Button extends React.Component {
handleClick(message) {
alert(message);
}
render() {
return (
<button onClick={this.handleClick.bind(this, 'Hello from
Button!')}>
Click Me
</button>
);
}
}
export default Button;
How it works:
The bind(this, 'Hello from Button!') method binds the
handleClick method and passes 'Hello from Button!' as an argument.
When the button is clicked, it will trigger the handleClick method with the
message passed as an argument.
Use Custom Hooks for Event Listeners
In React, using custom hooks to manage event listeners is a cleaner and reusable approach
compared to adding event listeners inside components.
By Chris H (Lycee APADE) Page 56
57 | P a g e
1. Why Use a Custom Hook for Event Listeners?
Encapsulation – Keeps event logic separate from component logic.
Reusability – Easily reuse the hook across multiple components.
Automatic Cleanup – Ensures event listeners are removed when components unmount.
2. Example: useEventListener Custom Hook
This hook allows you to add and remove event listeners dynamically.
useEventListener Hook
import { useEffect } from "react";
function useEventListener(eventName, handler, element = window) {
useEffect(() => {
if (!element) return;
element.addEventListener(eventName, handler);
return () => {
element.removeEventListener(eventName, handler);
};
}, [eventName, handler, element]);
}
export default useEventListener;
How it works:
Takes three parameters:
o eventName → Name of the event (e.g., "click", "keydown",
"resize").
o handler → The function to execute when the event occurs.
o element (default = window) → The target element to attach the event
listener to.
Uses useEffect to attach the event listener when the component mounts and
remove it when it unmounts.
By Chris H (Lycee APADE) Page 57
58 | P a g e
3. Example: Using useEventListener in a Component
Example: Listening for keydown Event
import React, { useState } from "react";
import useEventListener from "./useEventListener";
function KeyPressComponent() {
const [key, setKey] = useState("");
const handleKeyPress = (event) => {
setKey(event.key);
};
useEventListener("keydown", handleKeyPress);
return <h2>Pressed Key: {key}</h2>;
}
export default KeyPressComponent;
How it works:
The useEventListener hook listens for keyboard presses (keydown).
Whenever a key is pressed, handleKeyPress updates the state with
the pressed key.
4. Example: Handling click Events on the Window
import React, { useState } from "react";
import useEventListener from "./useEventListener";
function ClickTracker() {
const [clicks, setClicks] = useState(0);
const handleClick = () => {
setClicks((prev) => prev + 1);
};
useEventListener("click", handleClick);
return <h2>Window Clicks: {clicks}</h2>;
}
export default ClickTracker;
How it works:
The event listener listens for clicks anywhere on the window.
By Chris H (Lycee APADE) Page 58
59 | P a g e
When a click happens, the count updates.
5. Example: Listening for resize Events
import React, { useState } from "react";
import useEventListener from "./useEventListener";
function WindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
useEventListener("resize", handleResize);
return (
<h2>
Window Size: {size.width} x {size.height}
</h2>
);
}
export default WindowSize;
How it works:
Listens for window resize events and updates state accordingly.
6. Example: Using Event Listeners on a Specific Element
You can attach event listeners to a specific element instead of the window.
import React, { useRef, useState } from "react";
import useEventListener from "./useEventListener";
function ButtonClickCounter() {
const [count, setCount] = useState(0);
const buttonRef = useRef(null);
useEventListener(
By Chris H (Lycee APADE) Page 59
60 | P a g e
"click",
() => setCount((prev) => prev + 1),
buttonRef.current
);
return (
<button ref={buttonRef}>
Button Clicks: {count}
</button>
);
}
export default ButtonClickCounter;
How it works:
Attaches the event listener only to a specific button, instead of the whole
window.
Uses useRef to reference the button and pass it to useEventListener.
7. Enhancing useEventListener with Dynamic Listeners
If the event handler function changes frequently, use useCallback to prevent
unnecessary re-registrations.
import { useEffect, useRef } from "react";
function useEventListener(eventName, handler, element = window) {
const savedHandler = useRef();
useEffect(() => {
savedHandler.current = handler;
}, [handler]);
useEffect(() => {
if (!element) return;
const eventListener = (event) => savedHandler.current(event);
element.addEventListener(eventName, eventListener);
return () => {
element.removeEventListener(eventName, eventListener);
};
}, [eventName, element]);
}
export default useEventListener;
By Chris H (Lycee APADE) Page 60
61 | P a g e
Why use useRef()?
Ensures the latest event handler is always used without re-adding the event
listener on every render.
Improves performance by reducing unnecessary effect re-runs.
Handling Events on Dynamic Lists
When dealing with dynamic lists in React, handling events efficiently ensures smooth user
interaction. Whether you're adding, removing, or updating items, event handlers must be
properly assigned.
1. Common Scenarios for Event Handling in Lists
Clicking on list items (e.g., selecting or deleting an item
Updating an item dynamically
Attaching event listeners dynamically
2. Example: Handling Click Events on a List of Items
When rendering a dynamic list, you can attach event handlers directly to each
item using .map().
Click to Delete an Item
import React, { useState } from "react";
function DynamicList() {
const [items, setItems] = useState(["Apple", "Banana",
"Cherry"]);
const handleDelete = (index) => {
setItems(items.filter((_, i) => i !== index));
};
return (
<ul>
{items.map((item, index) => (
<li key={index} onClick={() => handleDelete(index)}>
{item} ❌
</li>
))}
</ul>
);
By Chris H (Lycee APADE) Page 61
62 | P a g e
export default DynamicList;
How it works:
The handleDelete function removes an item when clicked.
The onClick={() => handleDelete(index)} handler ensures the correct
item is deleted.
3. Example: Handling Input Changes in a Dynamic List
If your list includes inputs (e.g., an editable to-do list), you need to track changes
properly.
Editable List
import React, { useState } from "react";
function EditableList() {
const [items, setItems] = useState(["Task 1", "Task 2", "Task
3"]);
const handleChange = (index, event) => {
const newItems = [...items];
newItems[index] = event.target.value;
setItems(newItems);
};
return (
<ul>
{items.map((item, index) => (
<li key={index}>
<input
type="text"
value={item}
onChange={(event) => handleChange(index, event)}
/>
</li>
))}
</ul>
);
}
export default EditableList;
How it works:
The handleChange function updates the correct item using index.
Controlled inputs ensure the state is always in sync.
By Chris H (Lycee APADE) Page 62
63 | P a g e
4. Example: Handling Button Clicks in a List
If each list item has its own button (e.g., an add to cart button), ensure that the
correct item is passed to the event handler.
Click to Add to Cart
import React, { useState } from "react";
function ProductList() {
const products = ["Laptop", "Phone", "Headphones"];
const [cart, setCart] = useState([]);
const handleAddToCart = (product) => {
setCart([...cart, product]);
};
return (
<div>
<ul>
{products.map((product, index) => (
<li key={index}>
{product}
<button onClick={() => handleAddToCart(product)}>❌
Add</button>
</li>
))}
</ul>
<h3>Cart: {cart.join(", ")}</h3>
</div>
);
}
export default ProductList;
How it works:
The handleAddToCart function updates the cart with the clicked product.
Each button passes its corresponding product to the function.
5. Using Event Delegation for Performance Optimization
For large lists, attaching an event listener to every item can be inefficient. Event
delegation attaches the event to the parent container instead.
Example: Click to Highlight a List Item
import React, { useState } from "react";
function EventDelegationList() {
By Chris H (Lycee APADE) Page 63
64 | P a g e
const items = ["React", "Vue", "Angular"];
const [selected, setSelected] = useState(null);
const handleClick = (event) => {
if (event.target.tagName === "LI") {
setSelected(event.target.textContent);
}
};
return (
<div>
<ul onClick={handleClick}>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
{selected && <p>Selected: {selected}</p>}
</div>
);
}
export default EventDelegationList;
How it works:
The onClick is attached to the <ul> instead of each <li>.
The event.target.tagName === "LI" ensures clicks only register on list
items.
Better performance for large lists.
6. Example: Drag and Drop in Dynamic Lists
To make a list interactive, drag and drop functionality can be added.
Drag & Drop Example
import React, { useState } from "react";
function DragDropList() {
const [items, setItems] = useState(["Item 1", "Item 2", "Item
3"]);
const [draggedItem, setDraggedItem] = useState(null);
const handleDragStart = (index) => {
setDraggedItem(index);
};
const handleDrop = (index) => {
const newItems = [...items];
By Chris H (Lycee APADE) Page 64
65 | P a g e
const movedItem = newItems.splice(draggedItem, 1)[0];
newItems.splice(index, 0, movedItem);
setItems(newItems);
setDraggedItem(null);
};
return (
<ul>
{items.map((item, index) => (
<li
key={index}
draggable
onDragStart={() => handleDragStart(index)}
onDragOver={(e) => e.preventDefault()}
onDrop={() => handleDrop(index)}
style={{ border: "1px solid black", padding: "8px",
margin: "5px" }}
>
{item}
</li>
))}
</ul>
);
}
export default DragDropList;
How it works:
draggable allows list items to be dragged.
onDragStart stores the index of the dragged item.
onDrop moves the item to a new position.
Objective: API integration is properly implemented based on user requirements
By Chris H (Lycee APADE) Page 65
66 | P a g e
Implementation of API integration
Initial Setup and Planning
Describe API
React provides various APIs to help developers build dynamic and efficient UI components.
These APIs include built-in React methods, hooks, context, refs, and event handling
mechanisms.
API (Application Programming Interface) is a set of rules and protocols that allows different
software applications to communicate with each other. It defines how requests and responses
should be structured between systems.
Dependencies Installation (Axios)
Axios is a popular JavaScript library for making HTTP requests from a React application.
It simplifies API calls and handles features like automatic JSON parsing,
request/response interception, and error handling.
1. Installing Axios in React
Install via npm (Node Package Manager)
Run this command in your React project directory:
npm install axios
Install via yarn (Alternative)
yarn add axios
By Chris H (Lycee APADE) Page 66
67 | P a g e
2. Using Axios in React (GET Request Example)
Once installed, you can import Axios and use it in your components:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
function FetchUsers() {
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => setUsers(response.data))
.catch(error => console.error('Error fetching data:',
error));
}, []);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default FetchUsers;
axios.get(url) makes a GET request.
Handles errors with .catch().
3. Making a POST Request with Axios
import axios from 'axios';
import { useState } from 'react';
function CreateUser() {
const [name, setName] = useState('');
const handleSubmit = async () => {
try {
const response = await
axios.post('https://jsonplaceholder.typicode.com/users', {
name: name
});
console.log('User created:', response.data);
} catch (error) {
console.error('Error creating user:', error);
}
};
By Chris H (Lycee APADE) Page 67
68 | P a g e
return (
<div>
<input type="text" value={name} onChange={e =>
setName(e.target.value)} />
<button onClick={handleSubmit}>Create User</button>
</div>
);
}
export default CreateUser;
✅ axios.post(url, data) sends data to the server.
✅ Handles API errors using try...catch.
4. Using Axios with Async/Await (Better Readability)
import axios from 'axios';
import { useEffect, useState } from 'react';
function FetchData() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await
axios.get('https://jsonplaceholder.typicode.com/todos/1');
setData(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
export default FetchData;
✅ async/await makes code cleaner.
✅ Uses try...catch for better error handling.
5. Setting Global Axios Defaults
You can set base URLs and headers globally:
import axios from 'axios';
By Chris H (Lycee APADE) Page 68
69 | P a g e
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = 'Bearer
YOUR_TOKEN';
✅ This avoids repeating base URLs in each request.
✅ Useful for authentication tokens.
6. Handling Errors in Axios
axios.get('https://api.example.com/data')
.then(response => console.log(response.data))
.catch(error => {
if (error.response) {
console.error('Server Error:', error.response.data);
} else if (error.request) {
console.error('No Response:', error.request);
} else {
console.error('Error:', error.message);
}
});
✅ detects different types of errors (server error, network issues, etc.).
Organizing API Calls
When working with APIs in React, it’s important to structure your API calls properly. Let’s go
step by step:
1. Defining and Grouping API Calls
a. Why Group API Calls?
Improves code maintainability.
Keeps API logic separate from UI components.
Makes it easier to update or reuse API calls.
b. Creating an API Service File
Instead of writing API calls inside components, create a dedicated API service
module.
c. Define API Calls in a Separate File (apiService.js)
import axios from 'axios';
// Set up base URL for all requests
By Chris H (Lycee APADE) Page 69
70 | P a g e
const API = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 5000, // Optional: Set request timeout
});
// API Functions
export const fetchUsers = () => API.get('/users');
export const fetchUserById = (id) =>
API.get(`/users/${id}`);
export const createUser = (userData) => API.post('/users',
userData);
export const updateUser = (id, userData) =>
API.put(`/users/${id}`, userData);
export const deleteUser = (id) =>
API.delete(`/users/${id}`);
✅ All API calls are grouped in apiService.js.
✅ Components only call these functions instead of writing API logic inside
them.
2. Handling Data Fetching and Responses
Fetching Data in a React Component
Once API calls are grouped, use them inside components.
Fetch Users in a Component (UserList.js)
import React, { useEffect, useState } from 'react';
import { fetchUsers } from './apiService';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const getUsers = async () => {
try {
const response = await fetchUsers();
setUsers(response.data);
} catch (error) {
console.error('Error fetching users:', error);
} finally {
setLoading(false);
}
};
getUsers();
}, []);
return (
<div>
By Chris H (Lycee APADE) Page 70
71 | P a g e
{loading ? <p>Loading...</p> : users.map(user => <p
key={user.id}>{user.name}</p>)}
</div>
);
}
export default UserList;
✅ Uses useEffect for data fetching.
✅ Uses finally to handle loading state cleanup.
3. Error Handling in Axios
Handling API Errors Gracefully
Errors can occur due to network issues, wrong endpoints, or server failures.
Improve Error Handling in apiService.js
export const fetchUsers = async () => {
try {
const response = await API.get('/users');
return response.data;
} catch (error) {
handleApiError(error);
throw error; // Rethrow for component-level handling
}
};
// Generic error handler
const handleApiError = (error) => {
if (error.response) {
console.error(`Server Error (${error.response.status}):`,
error.response.data);
} else if (error.request) {
console.error('No Response from Server:', error.request);
} else {
console.error('Unexpected Error:', error.message);
}
};
✅ Categorizes server errors, network failures, and unexpected issues.
✅ Logs detailed error messages for debugging.
4. Asynchronous Handling and Concurrency
Handling Multiple API Calls Simultaneously
Sometimes, you need to fetch multiple datasets at the same time.
By Chris H (Lycee APADE) Page 71
72 | P a g e
Using Promise.all() to Fetch Multiple Resources
import { useEffect, useState } from 'react';
import { fetchUsers, fetchUserById } from './apiService';
function Dashboard() {
const [users, setUsers] = useState([]);
const [userDetail, setUserDetail] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const [usersResponse, userDetailResponse] = await
Promise.all([
fetchUsers(),
fetchUserById(1),
]);
setUsers(usersResponse.data);
setUserDetail(userDetailResponse.data);
} catch (error) {
console.error('Error fetching dashboard data:', error);
}
};
fetchData();
}, []);
return (
<div>
<h1>Dashboard</h1>
<h2>Users: {users.length}</h2>
<h3>First User: {userDetail?.name}</h3>
</div>
);
}
export default Dashboard;
✅ Promise.all() fetches multiple API calls concurrently, reducing load time.
✅ Improves performance over sequential API calls.
Using Axios Interceptors for Global Request Handling
Interceptors help handle authentication tokens and global error handling.
Add Interceptors in apiService.js
API.interceptors.request.use(
(config) => {
config.headers.Authorization = `Bearer YOUR_ACCESS_TOKEN`;
return config;
},
(error) => Promise.reject(error)
By Chris H (Lycee APADE) Page 72
73 | P a g e
);
API.interceptors.response.use(
(response) => response,
(error) => {
handleApiError(error);
return Promise.reject(error);
}
);
Performing API Security and testing
When integrating APIs in React, ensuring security and proper testing is crucial to prevent
vulnerabilities and ensure reliable functionality.
1. API Security in React
Common API Security Threats
Exposed API Keys in frontend code
Cross-Site Scripting (XSS) attacks
Cross-Site Request Forgery (CSRF) attacks
Man-in-the-Middle (MITM) attacks
Unauthorized Access (Broken Authentication)
By Chris H (Lycee APADE) Page 73
74 | P a g e
Best Practices for Securing API Calls
1. Store API Keys Securely
Never expose API keys directly in your React app! Use environment variables instead.
Use .env files to store API keys
Create a .env file in your project root:
REACT_APP_API_KEY=your_secret_api_key
REACT_APP_API_URL=https://api.example.com
Then, access it in your code:
const API_URL = process.env.REACT_APP_API_URL;
const API_KEY = process.env.REACT_APP_API_KEY;
✅ Prevents exposing API keys in public repositories.
2. Use HTTPS Instead of HTTP
Always use HTTPS to encrypt requests and prevent Man-in-the-Middle (MITM) attacks.
const response = await fetch('https://secure-api.com/data');
✅ Ensures data is encrypted during transmission.
3. Implement Authentication (JWT, OAuth, API Keys)
Many APIs require authentication.
Using JWT (JSON Web Token) in API Calls
const token = localStorage.getItem('authToken');
axios.get('https://api.example.com/protected-data', {
headers: {
Authorization: `Bearer ${token}`,
},
});
By Chris H (Lycee APADE) Page 74
75 | P a g e
✅Prevents unauthorized access.
4. Avoid CORS Issues (Use a Proxy or CORS Headers)
If your API has CORS (Cross-Origin Resource Sharing) issues, use a proxy in
package.json:
"proxy": "https://api.example.com"
Or handle it on the backend by setting headers:
Access-Control-Allow-Origin: *
✅Fixes cross-origin request issues in React apps.
5. Use API Rate Limiting & Throttling
To prevent API abuse, use rate limiting on your backend or use throttling in React.
Throttle API Calls in React using Lodash
import { useCallback } from 'react';
import { debounce } from 'lodash';
const searchAPI = debounce(async (query) => {
const response = await axios.get(`/search?q=${query}`);
console.log(response.data);
}, 500); // Wait 500ms before making the API call
const handleSearch = (e) => searchAPI(e.target.value);
✅Prevents API spamming from fast user inputs.
6. Protect Against XSS & CSRF Attacks
To prevent XSS, always sanitize user inputs before sending data to the API.
Use DOMPurify to sanitize user input
import DOMPurify from 'dompurify';
const safeInput = DOMPurify.sanitize(userInput);
By Chris H (Lycee APADE) Page 75
76 | P a g e
✅Prevents malicious script injections.
2. API Testing in React ��
Testing your API calls ensures they work correctly before integrating them into
the UI.
Testing API Calls with Postman
1. Open Postman and enter the API URL.
2. Set HTTP method (GET, POST, PUT, DELETE).
3. Add headers (e.g., Authorization: Bearer token).
4. Send request & inspect response.
✅Verifies API response before React integration.
Writing Unit Tests for API Calls in React
For unit testing, use Jest & React Testing Library.
Mock API Calls in Jest (apiService.test.js)
import axios from 'axios';
import { fetchUsers } from './apiService';
jest.mock('axios'); // Mock Axios
test('fetchUsers should return user data', async () => {
axios.get.mockResolvedValue({ data: [{ id: 1, name: 'John Doe' }] });
const users = await fetchUsers();
expect(users.data).toEqual([{ id: 1, name: 'John Doe' }]);
});
✅Ensures API function works correctly before UI testing.
Testing API Calls in React Components
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';
import { fetchUsers } from './apiService';
jest.mock('./apiService');
test('renders fetched user list', async () => {
fetchUsers.mockResolvedValue({ data: [{ id: 1, name: 'John Doe' }] });
render(<UserList />);
By Chris H (Lycee APADE) Page 76
77 | P a g e
await waitFor(() => expect(screen.getByText('John
Doe')).toBeInTheDocument());
});
✅Verifies UI updates based on API response.
Automating API Tests with Cypress
Cypress allows end-to-end API testing in a browser.
✅Example: Cypress API Test (api.spec.js)
describe('API Testing with Cypress', () => {
it('Fetches users successfully', () => {
cy.request('GET', 'https://jsonplaceholder.typicode.com/users')
.its('status')
.should('eq', 200);
});
});
✅Runs API tests automatically in a browser.
By Chris H (Lycee APADE) Page 77
78 | P a g e
Apply Tailwind CSS framework (10 Hours)
Objective: Tailwind utility classes are correctly Applied based on UI Design.
Integrating of Tailwind CSS in React.JS
Install Tailwind CSS
To install Tailwind CSS, follow these steps based on your preferred setup:
1. Install via npm (Recommended)
This method is best for frameworks like React, Next.js, Vue, or traditional HTML projects.
Step 1: Initialize a project
If you haven't already, create a project folder and navigate to it:
mkdir my-project && cd my-project
npm init -y
Step 2: Install Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
Step 3: Generate the Tailwind config file
npx tailwindcss init -p
This creates tailwind.config.js and postcss.config.js.
Step 4: Configure Tailwind to remove unused styles
Edit tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js}"], // Adjust based on your project
structure
theme: {
extend: {},
},
plugins: [],
};
By Chris H (Lycee APADE) Page 78
79 | P a g e
Step 5: Add Tailwind to your CSS
Create a CSS file (e.g., styles.css) and add:
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 6: Build Tailwind CSS
Run:
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watch
Make sure your HTML references dist/output.css.
2. Install via CDN (Quick Setup)
For quick prototyping, add this in your HTML <head>:
<link
href="https://cdn.jsdelivr.net/npm/tailwindcss@3.4.1/dist/tailwind.min.css"
rel="stylesheet">
No build step is required, but customization is limited.
Configuring Tailwind CSS
1. Generate Tailwind Config File
If you haven't already, run:
npx tailwindcss init
This creates a default tailwind.config.js file.
For advanced configurations (PostCSS included), use:
npx tailwindcss init -p
By Chris H (Lycee APADE) Page 79
80 | P a g e
2. Customize the Configuration File
Open tailwind.config.js. It looks like this by default:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [],
};
a) Specify Content Paths
Tell Tailwind which files to scan for class usage (prevents unused styles from being purged):
module.exports = {
content: [
"./index.html",
"./src/**/*.{html,js,jsx,ts,tsx,vue}"
],
theme: {
extend: {},
},
plugins: [],
};
b) Extend the Theme
Modify colors, fonts, spacing, breakpoints, etc.
module.exports = {
theme: {
extend: {
colors: {
primary: '#1E40AF', // Custom blue
secondary: '#F43F5E', // Custom pink
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
serif: ['Merriweather', 'serif'],
},
spacing: {
'128': '32rem',
'144': '36rem',
},
},
},
};
By Chris H (Lycee APADE) Page 80
81 | P a g e
c) Enable Dark Mode
Tailwind supports dark mode with "class" or "media" strategies.
module.exports = {
darkMode: "class", // "media" uses system settings
};
"media": Uses OS-level dark mode settings.
"class": Requires manually adding class="dark" to enable dark mode.
Example usage in HTML:
<html class="dark">
<body class="bg-gray-900 text-white">
<h1 class="text-primary">Hello, Dark Mode!</h1>
</body>
</html>
d) Add Custom Screens (Breakpoints)
Define or modify responsive breakpoints.
module.exports = {
theme: {
screens: {
'xs': '475px', // Custom extra small screen
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
},
},
};
e) Add Plugins
Tailwind has useful plugins like forms, typography, and aspect ratio.
npm install -D @tailwindcss/forms @tailwindcss/typography
@tailwindcss/aspect-ratio
Then, update tailwind.config.js:
module.exports = {
plugins: [
require('@tailwindcss/forms'),
By Chris H (Lycee APADE) Page 81
82 | P a g e
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
};
3. Run Tailwind CSS
Ensure Tailwind processes your styles by running:
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watch
This continuously watches for file changes.
Using Utility-First Fundamentals
Tailwind CSS is a utility-first framework, meaning it provides low-level utility classes to build
designs directly in your HTML without writing custom CSS.
1. Why Use Utility-First CSS?
✅Faster Development: No need to write separate CSS files.
✅Better Maintainability: Styles are directly in HTML.
✅Flexible and Scalable: Avoids opinionated components.
✅Eliminates Unused CSS: Tailwind removes unused styles in production.
2. Basic Utility Classes
Tailwind provides pre-defined classes for styling elements. You compose these classes in HTML
elements.
a) Sizing & Spacing
Width & Height (w-{size}, h-{size}):
<div class="w-64 h-32 bg-blue-500"></div>
Padding & Margin (p-{size}, m-{size}):
<div class="p-4 m-2 bg-gray-200">Padding & Margin</div>
By Chris H (Lycee APADE) Page 82
83 | P a g e
Negative Margins (-m-{size}):
<div class="-mt-4">Moves up</div>
b) Typography
Font Size (text-{size}):
<p class="text-lg">Large text</p>
Font Weight (font-{weight}):
<p class="font-bold">Bold text</p>
Text Alignment (text-{left|center|right}):
<p class="text-center">Centered text</p>
Line Height & Letter Spacing:
<p class="leading-loose tracking-wide">Better readability</p>
c) Backgrounds & Borders
Background Colors (bg-{color}):
<div class="bg-red-500 text-white p-4">Red Background</div>
Border & Radius:
<div class="border border-gray-400 rounded-lg p-4">Bordered box</div>
d) Flexbox & Grid
Flexbox Utilities:
<div class="flex items-center justify-between">
<span>Item 1</span>
<span>Item 2</span>
</div>
Grid Layout:
<div class="grid grid-cols-3 gap-4">
<div class="bg-gray-200 p-4">1</div>
<div class="bg-gray-200 p-4">2</div>
<div class="bg-gray-200 p-4">3</div>
By Chris H (Lycee APADE) Page 83
84 | P a g e
</div>
e) Responsive Design
Tailwind uses mobile-first breakpoints:
Example:
<p class="text-sm md:text-lg lg:text-2xl">Responsive text</p>
f) Hover & Focus States
Hover Effect:
<button class="bg-blue-500 hover:bg-blue-700 text-white p-2">Hover
Me</button>
Focus & Active States:
<input class="border border-gray-300 focus:border-blue-500"
placeholder="Focus me">
g) Dark Mode
If darkMode: "class" is enabled in tailwind.config.js:
<div class="bg-white dark:bg-gray-800 text-black dark:text-white">
Dark mode enabled!
</div>
3. Composition: Extracting Components
Use @apply in a CSS file for reusability:
By Chris H (Lycee APADE) Page 84
85 | P a g e
.btn {
@apply px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-700;
}
HTML usage:
<button class="btn">Click Me</button>
5. Building a Simple Card
<div class="max-w-sm rounded-lg shadow-lg overflow-hidden bg-white p-6">
<h2 class="text-xl font-semibold">Tailwind Card</h2>
<p class="text-gray-600">This is a simple card built using Tailwind
CSS.</p>
<button class="mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-
blue-700">
Learn More
</button>
</div>
5. Running Tailwind in Watch Mode
If using a PostCSS setup, start Tailwind in watch mode:
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watch
Handling Hover, Focus, and Other States
Tailwind CSS provides state variants like hover:, focus:, active:, and more to handle
different user interactions. These variants allow you to style elements based on user actions
without writing custom CSS.
1. Hover States (hover:)
Apply styles when the user hovers over an element.
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4
rounded">
Hover Me
</button>
✅Explanation:
hover:bg-blue-700 changes the background color when hovered.
By Chris H (Lycee APADE) Page 85
86 | P a g e
2. Focus States (focus:)
Apply styles when an element (like an input) is focused.
<input class="border border-gray-300 focus:border-blue-500 focus:ring-2
focus:ring-blue-300 p-2 rounded" placeholder="Focus on me">
✅Explanation:
focus:border-blue-500 changes the border color on focus.
focus:ring-2 focus:ring-blue-300 adds a ring effect around the input.
3. Active States (active:)
Apply styles when an element is clicked.
<button class="bg-green-500 active:bg-green-700 text-white py-2 px-4
rounded">
Click Me
</button>
✅Explanation:
active:bg-green-700 changes the background color while the button is being clicked.
4. Disabled States (disabled:)
Apply styles to disabled elements.
<button class="bg-gray-400 text-white py-2 px-4 rounded disabled:opacity-50"
disabled>
Disabled Button
</button>
✅Explanation:
disabled:opacity-50 reduces opacity for a disabled button.
By Chris H (Lycee APADE) Page 86
87 | P a g e
5. Group Hover & Focus (group-hover: & group-focus:)
Used when styling child elements based on the parent's state.
<div class="group p-4 border border-gray-300 hover:bg-gray-100">
<p class="text-gray-700 group-hover:text-blue-500">Hover over the box</p>
</div>
✅Explanation:
group-hover:text-blue-500 changes text color when the parent div is hovered.
6. Focus Within (focus-within:)
Applies styles when any child inside an element receives focus.
<div class="border border-gray-300 p-4 focus-within:border-blue-500">
<input type="text" class="outline-none p-2 w-full" placeholder="Type
something...">
</div>
✅Explanation:
focus-within:border-blue-500 changes border color when the input inside the div is
focused.
7. First & Last Child (first: & last:)
Style the first or last child of a parent.
<ul>
<li class="first:text-red-500 last:text-blue-500">Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
✅Explanation:
first:text-red-500 makes the first item red.
last:text-blue-500 makes the last item blue.
By Chris H (Lycee APADE) Page 87
88 | P a g e
8. Even & Odd Child (even: & odd:)
Style alternating children.
html
CopyEdit
<ul>
<li class="odd:bg-gray-100 even:bg-gray-200 p-2">Item 1</li>
<li class="odd:bg-gray-100 even:bg-gray-200 p-2">Item 2</li>
<li class="odd:bg-gray-100 even:bg-gray-200 p-2">Item 3</li>
<li class="odd:bg-gray-100 even:bg-gray-200 p-2">Item 4</li>
</ul>
✅Explanation:
odd:bg-gray-100 applies a light gray background to odd items.
even:bg-gray-200 applies a slightly darker gray to even items.
9. Motion & Interaction States
a) Focus Visible (focus-visible:)
Only applies styles when navigating with a keyboard (not mouse clicks).
<button class="focus-visible:ring-4 focus-visible:ring-blue-500 p-2 bg-gray-
300 rounded">
Keyboard Focus Me
</button>
b) Motion Reduce (motion-reduce:)
Respects the user's "reduce motion" setting.
<div class="animate-bounce motion-reduce:animate-none">
❌
</div>
✅Explanation:
motion-reduce:animate-none disables animation for users who prefer reduced motion.
10. Combining Multiple States
You can combine multiple states like this:
By Chris H (Lycee APADE) Page 88
89 | P a g e
<button class="bg-purple-500 hover:bg-purple-700 focus:ring-4 focus:ring-
purple-300 active:bg-purple-900 text-white py-2 px-4 rounded">
Interactive Button
</button>
✅Explanation:
hover:bg-purple-700: Changes color on hover.
focus:ring-4 focus:ring-purple-300: Adds a ring effect on focus.
active:bg-purple-900: Darkens color when clicked.
✔ Animation and Transitions
Tailwind CSS provides built-in animations and transitions to create smooth, interactive effects
without writing custom CSS.
By Chris H (Lycee APADE) Page 89
90 | P a g e
1. Transitions (transition)
The transition utility allows you to animate changes in properties like opacity, color,
transform, etc.
Basic Example
<button class="bg-blue-500 text-white px-4 py-2 rounded transition duration-
300 hover:bg-blue-700">
Hover Me
</button>
✅Explanation:
transition: Enables transitions.
duration-300: Sets animation duration to 300ms.
hover:bg-blue-700: Changes background color on hover.
Controlling Transition Speed
Use duration-{time} to control speed (default unit: ms).
<div class="transition duration-700">Slow (700ms)</div>
<div class="transition duration-100">Fast (100ms)</div>
Applying Transitions to Specific Properties
html
CopyEdit
<button class="transition-colors duration-300 hover:text-red-500 hover:bg-
gray-200">
Color Transition
</button>
✅Available properties:
transition-all (default)
transition-colors
transition-opacity
transition-transform
transition-shadow
2. Easing Functions (ease-)
Controls how an animation speeds up or slows down.
<button class="bg-green-500 text-white px-4 py-2 rounded transition ease-in-
out duration-500 hover:scale-110">
Smooth Scale
</button>
By Chris H (Lycee APADE) Page 90
91 | P a g e
Tailwind CSS: Animation & Transitions ��
Tailwind CSS provides built-in animations and transitions to create smooth, interactive effects
without writing custom CSS.
1. Transitions (transition)
The transition utility allows you to animate changes in properties like opacity, color,
transform, etc.
Basic Example
<button class="bg-blue-500 text-white px-4 py-2 rounded transition duration-
300 hover:bg-blue-700">
Hover Me
</button>
✅Explanation:
transition: Enables transitions.
duration-300: Sets animation duration to 300ms.
hover:bg-blue-700: Changes background color on hover.
Controlling Transition Speed
Use duration-{time} to control speed (default unit: ms).
<div class="transition duration-700">Slow (700ms)</div>
<div class="transition duration-100">Fast (100ms)</div>
By Chris H (Lycee APADE) Page 91
92 | P a g e
Applying Transitions to Specific Properties
<button class="transition-colors duration-300 hover:text-red-500 hover:bg-
gray-200">
Color Transition
</button>
✅Available properties:
transition-all (default)
transition-colors
transition-opacity
transition-transform
transition-shadow
2. Easing Functions (ease-)
Controls how an animation speeds up or slows down.
<button class="bg-green-500 text-white px-4 py-2 rounded transition ease-in-
out duration-500 hover:scale-110">
Smooth Scale
</button>
✅Available options:
Class Effect
ease-in Starts slow, speeds up
ease-out Starts fast, slows down
ease-in-out Slow start & end
ease-linear Constant speed
3. Transformations (scale, rotate, translate, skew)
You can animate transformations with transition-transform.
Scaling Elements (scale-)
<div class="transition-transform duration-500 hover:scale-125">
Hover to Scale Up
By Chris H (Lycee APADE) Page 92
93 | P a g e
</div>
Rotating Elements (rotate-)
<button class="transition-transform duration-500 hover:rotate-45">
Rotate Me
</button>
Moving Elements (translate-)
<div class="transition-transform duration-500 hover:translate-x-10">
Move Right
</div>
Skewing Elements (skew-)
<div class="transition-transform duration-500 hover:skew-y-6">
Skew Me
</div>
4. Built-in Animations (animate-)
Tailwind provides predefined animations for common effects.
a) Fade In & Out (animate-fade)
<div class="animate-fade">Fading Effect</div>
b) Bounce Effect (animate-bounce)
<div class="animate-bounce">I bounce!</div>
c) Spinning Animation (animate-spin)
<div class="animate-spin w-10 h-10 border-4 border-blue-500 border-t-
transparent rounded-full"></div>
d) Ping Animation (animate-ping)
Creates a pulsing effect.
<div class="w-4 h-4 bg-red-500 rounded-full animate-ping"></div>
e) Pulse Animation (animate-pulse)
A smooth fading animation.
<div class="w-10 h-10 bg-gray-300 animate-pulse"></div>
f) Wiggle / Shake Animation
<div class="animate-[wiggle_1s_ease-in-out_infinite]">Wiggle Me</div>
✅Custom animations like wiggle require adding styles in tailwind.config.js (explained
below).
By Chris H (Lycee APADE) Page 93
94 | P a g e
5. Custom Keyframe Animations
You can define custom animations in tailwind.config.js.
Example: Custom Wiggle Animation
Step 1: Add Custom Animation in tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
wiggle: 'wiggle 1s ease-in-out infinite',
},
keyframes: {
wiggle: {
'0%, 100%': { transform: 'rotate(-3deg)' },
'50%': { transform: 'rotate(3deg)' },
},
},
},
},
plugins: [],
};
Step 2: Use the Animation in HTML
<div class="animate-wiggle">Wiggle Me</div>
6. Combining Animations & Transitions
You can mix animations, transitions, and transforms.
<button class="bg-purple-500 text-white px-4 py-2 rounded transition-all
duration-500 ease-in-out hover:scale-110 hover:rotate-6 hover:bg-purple-700">
Hover Me
</button>
✅What happens?
The button scales up, rotates, and changes color on hover.
7. Motion Preference (motion-reduce:)
Respects users with reduced motion preferences.
<div class="animate-bounce motion-reduce:animate-none">
Respect Motion Preferences
</div>
By Chris H (Lycee APADE) Page 94
95 | P a g e
✅ If a user has reduced motion enabled, the bounce effect is removed.
✔ Flexbox and Grid
Tailwind CSS makes it super easy to create flexible and responsive layouts using Flexbox and
Grid utilities. Let’s dive into how you can use them efficiently.
1. Flexbox (flex)
The flex utility helps in creating one-dimensional layouts where items align horizontally or
vertically.
Basic Flexbox Layout
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">Item 1</div>
<div class="bg-green-500 text-white p-4">Item 2</div>
<div class="bg-red-500 text-white p-4">Item 3</div>
</div>
✅Explanation:
flex makes the container a flexbox.
space-x-4 adds horizontal spacing.
The items distribute in a row by default.
By Chris H (Lycee APADE) Page 95
96 | P a g e
2. Flex Direction (flex-row, flex-col)
Controls the direction of flex items.
<div class="flex flex-col space-y-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">Item 1</div>
<div class="bg-green-500 text-white p-4">Item 2</div>
<div class="bg-red-500 text-white p-4">Item 3</div>
</div>
3. Aligning Items (justify-* & items-*)
Justify Content (Horizontal Alignment)
Class Effect
justify-start Aligns left (default)
justify-center Centers items
justify-end Aligns right
justify-between Spaces items evenly
justify-around Spaces with padding
justify-evenly Equal spacing
<div class="flex justify-center bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">Centered Item</div>
</div>
By Chris H (Lycee APADE) Page 96
97 | P a g e
Align Items (Vertical Alignment)
<div class="flex h-32 items-center bg-gray-200">
<div class="bg-blue-500 text-white p-4">Vertically Centered</div>
</div>
4. Flex Grow, Shrink & Basis
Flex Grow (flex-grow)
Allows an item to expand and take up available space.
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 flex-grow">Item 1</div>
<div class="bg-green-500 text-white p-4">Item 2</div>
</div>
Flex Shrink (flex-shrink)
Prevents an item from shrinking when space is limited.
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 flex-shrink-0 w-40">No Shrink</div>
<div class="bg-green-500 text-white p-4">Shrinks</div>
</div>
Flex Basis (basis-*)
Sets the initial size before items grow/shrink.
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 basis-1/2">Half Width</div>
<div class="bg-green-500 text-white p-4">Auto Size</div>
</div>
5. Wrapping Flex Items (flex-wrap)
Class Effect
By Chris H (Lycee APADE) Page 97
98 | P a g e
Class Effect
flex-nowrap Default (no wrapping)
flex-wrap Wraps items to new lines
flex-wrap-reverse Wraps in reverse order
<div class="flex flex-wrap gap-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 w-32">Item 1</div>
<div class="bg-green-500 text-white p-4 w-32">Item 2</div>
<div class="bg-red-500 text-white p-4 w-32">Item 3</div>
<div class="bg-purple-500 text-white p-4 w-32">Item 4</div>
</div>
6. CSS Grid (grid)
The grid utility helps in creating two-dimensional layouts.
Basic Grid Layout
<div class="grid grid-cols-3 gap-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">1</div>
<div class="bg-green-500 text-white p-4">2</div>
<div class="bg-red-500 text-white p-4">3</div>
</div>
✅Explanation:
grid: Enables CSS Grid.
grid-cols-3: Creates 3 equal columns.
gap-4: Adds spacing between items.
Tailwind CSS: Flexbox & Grid �
Tailwind CSS makes it super easy to create flexible and responsive layouts using Flexbox and
Grid utilities. Let’s dive into how you can use them efficiently.
By Chris H (Lycee APADE) Page 98
99 | P a g e
1.Flexbox (flex)
The flex utility helps in creating one-dimensional layouts where items align horizontally or
vertically.
Basic Flexbox Layout
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">Item 1</div>
<div class="bg-green-500 text-white p-4">Item 2</div>
<div class="bg-red-500 text-white p-4">Item 3</div>
</div>
✅Explanation:
flex makes the container a flexbox.
space-x-4 adds horizontal spacing.
The items distribute in a row by default.
2 Flex Direction (flex-row, flex-col)
Controls the direction of flex items.
<div class="flex flex-col space-y-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">Item 1</div>
<div class="bg-green-500 text-white p-4">Item 2</div>
<div class="bg-red-500 text-white p-4">Item 3</div>
</div>
✅Variants:
Class Direction
flex-row Default (horizontal)
flex-col Stacks items vertically
flex-row-reverse Reverses row order
flex-col-reverse Reverses column order
By Chris H (Lycee APADE) Page 99
100 | P a g e
3 Aligning Items (justify-* & items-*)
Justify Content (Horizontal Alignment)
Class Effect
justify-start Aligns left (default)
justify-center Centers items
justify-end Aligns right
justify-between Spaces items evenly
justify-around Spaces with padding
justify-evenly Equal spacing
<div class="flex justify-center bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">Centered Item</div>
</div>
Align Items (Vertical Alignment)
Class Effect
items-start Aligns items to the top
items-center Centers items vertically
items-end Aligns items to the bottom
items-stretch Stretches items (default)
<div class="flex h-32 items-center bg-gray-200">
<div class="bg-blue-500 text-white p-4">Vertically Centered</div>
</div>
4 Flex Grow, Shrink & Basis
Flex Grow (flex-grow)
Allows an item to expand and take up available space.
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 flex-grow">Item 1</div>
By Chris H (Lycee APADE) Page 100
101 | P a g e
<div class="bg-green-500 text-white p-4">Item 2</div>
</div>
Flex Shrink (flex-shrink)
Prevents an item from shrinking when space is limited.
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 flex-shrink-0 w-40">No Shrink</div>
<div class="bg-green-500 text-white p-4">Shrinks</div>
</div>
Flex Basis (basis-*)
Sets the initial size before items grow/shrink.
<div class="flex space-x-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 basis-1/2">Half Width</div>
<div class="bg-green-500 text-white p-4">Auto Size</div>
</div>
5. Wrapping Flex Items (flex-wrap)
Class Effect
flex-nowrap Default (no wrapping)
flex-wrap Wraps items to new lines
flex-wrap-reverse Wraps in reverse order
<div class="flex flex-wrap gap-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4 w-32">Item 1</div>
<div class="bg-green-500 text-white p-4 w-32">Item 2</div>
<div class="bg-red-500 text-white p-4 w-32">Item 3</div>
<div class="bg-purple-500 text-white p-4 w-32">Item 4</div>
</div>
6 CSS Grid (grid)
The grid utility helps in creating two-dimensional layouts.
By Chris H (Lycee APADE) Page 101
102 | P a g e
Basic Grid Layout
<div class="grid grid-cols-3 gap-4 bg-gray-200 p-4">
<div class="bg-blue-500 text-white p-4">1</div>
<div class="bg-green-500 text-white p-4">2</div>
<div class="bg-red-500 text-white p-4">3</div>
</div>
✅Explanation:
grid: Enables CSS Grid.
grid-cols-3: Creates 3 equal columns.
gap-4: Adds spacing between items.
7 Controlling Columns (grid-cols-*)
<div class="grid grid-cols-2 gap-4">
<div class="bg-blue-500 text-white p-4">50% Width</div>
<div class="bg-green-500 text-white p-4">50% Width</div>
</div>
8 Controlling Rows (grid-rows-*)
<div class="grid grid-rows-3 gap-4">
<div class="bg-blue-500 text-white p-4">Row 1</div>
<div class="bg-green-500 text-white p-4">Row 2</div>
<div class="bg-red-500 text-white p-4">Row 3</div>
</div>
9 Spanning Items (col-span-*, row-span-*)
<div class="grid grid-cols-3 gap-4">
<div class="col-span-2 bg-blue-500 text-white p-4">Spans 2 Columns</div>
<div class="bg-green-500 text-white p-4">Item</div>
</div>
Responsive Flexbox & Grid
Tailwind supports responsive design using breakpoints:
By Chris H (Lycee APADE) Page 102
103 | P a g e
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-blue-500 text-white p-4">Responsive 1</div>
<div class="bg-green-500 text-white p-4">Responsive 2</div>
<div class="bg-red-500 text-white p-4">Responsive 3</div>
<div class="bg-purple-500 text-white p-4">Responsive 4</div>
</div>
✅Explanation:
1 column on small screens.
2 columns on sm (640px).
4 columns on lg (1024px).
Reusing Styles
Tailwind CSS is great for utility-first styling, but managing repeated classes across multiple
elements can get tricky. Thankfully, Tailwind provides several ways to reuse styles efficiently
while keeping your HTML clean and maintainable. Let’s explore the best methods!
1 Using @apply in CSS
The @apply directive allows you to extract reusable styles into a CSS file.
Example: Creating a Button Style
css
CopyEdit
/* styles.css */
.btn {
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-
700;
}
<button class="btn">Click Me</button>
By Chris H (Lycee APADE) Page 103
104 | P a g e
✅Why use @apply?
Keeps HTML cleaner.
Centralizes styling for easier updates.
Works well for theming and design consistency.
2 Extracting Components
If you’re using a frontend framework like React, Vue, or Svelte, create reusable components
instead of repeating Tailwind classes.
Example: Reusable Button Component in React
export default function Button({ text }) {
return (
<button className="bg-blue-500 text-white font-bold py-2 px-4 rounded
hover:bg-blue-700">
{text}
</button>
);
}
Now, use it anywhere:
<Button text="Click Me" />
✅Why use components?
Keeps HTML clean.
Easier to manage complex UI elements.
Works well in modern frontend frameworks.
3 Using Tailwind’s theme.extend for Custom Styles
You can extend Tailwind's default theme in tailwind.config.js to define reusable values for
colors, spacing, typography, etc.
Example: Custom Button Colors
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: "#1E40AF", // Custom blue color
By Chris H (Lycee APADE) Page 104
105 | P a g e
secondary: "#9333EA", // Custom purple
},
},
},
};
Now, use the colors like this:
<button class="bg-primary text-white px-4 py-2 rounded">Primary</button>
<button class="bg-secondary text-white px-4 py-2 rounded">Secondary</button>
✅Why use theme extensions?
Define your own brand colors, fonts, etc.
Makes your design system more consistent.
Allows easy global updates.
4 Creating Custom Utility Classes
Sometimes, you need a custom utility class that Tailwind doesn’t provide by default.
Example: Custom Shadow Utility
/* styles.css */
.shadow-custom {
@apply shadow-lg shadow-blue-500/50;
}
Use it in HTML:
<div class="shadow-custom p-4 bg-white rounded">Custom Shadow</div>
5 Variants with Tailwind Plugins
Use the variants feature in tailwind.config.js to define reusable hover, focus, and dark
mode styles.
By Chris H (Lycee APADE) Page 105
106 | P a g e
Example: Custom Hover Effect
// tailwind.config.js
module.exports = {
theme: {
extend: {},
},
plugins: [
function ({ addVariant }) {
addVariant("hover-zoom", ["&:hover"]);
},
],
};
Now, use the new hover-zoom class:
<img class="hover-zoom:scale-110 transition-transform duration-300"
src="image.jpg" />
6 Using Tailwind’s @layer for Better Organization
If you have many custom styles, Tailwind’s @layer directive helps organize styles into layers.
Example: Defining Components in @layer
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn {
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-
700;
}
}
Use it in HTML:
<button class="btn">Click Me</button>
✅Why use @layer?
Helps Tailwind remove unused CSS during production.
Keeps styling modular and organized.
7 Using Tailwind with CSS Modules (For Next.js & React)
If you're using Next.js or React, CSS Modules provide scoped styling.
By Chris H (Lycee APADE) Page 106
107 | P a g e
Example: Button.module.css
/* Button.module.css */
.btn {
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-
700;
}
Use it in a React component:
import styles from "./Button.module.css";
export default function Button() {
return <button className={styles.btn}>Click Me</button>;
}
✅Why use CSS Modules?
Avoids class name conflicts.
Works well with Tailwind’s @apply.
8 Creating Design Tokens (CSS Variables + Tailwind)
You can use CSS variables inside Tailwind for better design control.
Example: Defining Custom Colors in CSS
:root {
--primary-color: #1E40AF;
--secondary-color: #9333EA;
}
Use it in Tailwind:
<button class="bg-[var(--primary-color)] text-white px-4 py-2 rounded">
Primary Button
</button>
✅Why use CSS Variables?
Helps with theming.
Can be changed dynamically (e.g., dark mode).
By Chris H (Lycee APADE) Page 107
108 | P a g e
Tailwind CSS: Adding Custom Styles & Using Functions & Directives �
Tailwind CSS provides powerful ways to customize styles and enhance functionality using
built-in functions and directives. This guide covers how to add custom styles and how to use
Tailwind’s functions and directives effectively.
1. Adding Custom Styles in Tailwind CSS
Tailwind is utility-first, but sometimes you need custom styling. Here’s how you can do it
efficiently:
1 Using @apply to Reuse Tailwind Classes
You can use @apply inside a CSS file to group utility classes.
Example: Custom Button Styles
/* styles.css */
.btn {
@apply bg-blue-500 text-white font-bold py-3 px-6 rounded-lg hover:bg-blue-
700 transition;
}
Use it in your HTML:
<button class="btn">Click Me</button>
✅Why use @apply?
Keeps your HTML clean.
Groups related styles for consistency.
Makes maintenance easier.
2 Extending Tailwind’s Theme in tailwind.config.js
You can extend Tailwind’s default theme to add custom colors, fonts, spacing, etc.
Example: Adding Custom Colors
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: "#FF5733", // Custom brand color
dark: "#1A1A1A", // Custom dark theme color
By Chris H (Lycee APADE) Page 108
109 | P a g e
},
},
},
};
Now, use them like this:
<div class="bg-brand text-white p-4">Custom Brand Color</div>
<div class="bg-dark text-white p-4">Custom Dark Mode</div>
✅Why extend the theme?
Keeps colors consistent across your project.
No need to repeat HEX codes everywhere.
3 Creating Custom Utility Classes
Sometimes, you need a utility that Tailwind doesn’t provide by default.
Example: Custom Text Glow Effect
@layer utilities {
.text-glow {
text-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
}
}
Use it in HTML:
<p class="text-glow text-white">Glowing Text</p>
✅Why use custom utilities?
Keeps your codebase lean.
You don’t have to write raw CSS.
2. Functions & Directives in Tailwind CSS
Tailwind includes several powerful functions and directives to enhance customization.
By Chris H (Lycee APADE) Page 109
110 | P a g e
1 Using @layer for Better Organization
Tailwind organizes styles into three layers:
base → Resets and global styles
components → Custom reusable styles
utilities → Extra utility classes
Example: Organizing Custom Styles with @layer
@layer base {
h1 {
@apply text-3xl font-bold text-gray-900;
}
}
@layer components {
.btn {
@apply bg-blue-500 text-white font-bold py-3 px-6 rounded-lg hover:bg-
blue-700;
}
}
@layer utilities {
.text-glow {
text-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
}
}
✅Why use @layer?
Organizes styles for better maintainability.
Helps optimize CSS during production builds.
2 Using theme() Function
The theme() function lets you access Tailwind’s configuration values dynamically.
Example: Dynamic Font Sizes
.btn {
font-size: theme(fontSize.lg);
padding: theme(spacing.4) theme(spacing.6);
}
✅Why use theme ()?
By Chris H (Lycee APADE) Page 110
111 | P a g e
Ensures styles stay in sync with Tailwind’s theme.
Prevents hardcoding values manually.
3 Using @variants for Custom Variants
You can define custom variants like hover, focus, or even new ones.
Example: Custom Hover & Dark Mode Styles
@variants hover, focus {
.btn-outline {
@apply border border-blue-500 text-blue-500;
}
}
Now, this class will work with hover: and focus:
<button class="btn-outline hover:bg-blue-500 hover:text-white">Hover
Me</button>
✅Why use @variants?
Easily add new responsive states.
Keep utility-based styles consistent.
4 Adding Plugins for More Functionality
Tailwind allows plugins to extend its capabilities.
Example: Adding Forms Plugin
// tailwind.config.js
module.exports = {
plugins: [require("@tailwindcss/forms")], // Adds better form styling
};
Now, use it like this:
<input type="text" class="form-input" placeholder="Enter text">
✅Why use plugins?
Keeps Tailwind’s core clean.
Easily add missing features.
By Chris H (Lycee APADE) Page 111
112 | P a g e
5 Adding CSS Variables for Dynamic Styling
CSS variables make it easy to change themes dynamically.
Example: Custom Theme with CSS Variables
:root {
--primary-color: #4F46E5;
--secondary-color: #EC4899;
}
Use it in Tailwind:
<button class="bg-[var(--primary-color)] text-white p-3 rounded">
Primary Button
</button>
✅Why use CSS Variables?
Allows real-time theme changes.
Keeps styling more dynamic.
Objective: Responsive design principles are properly applied according to the required design
Applying responsive design principles
1. Mobile-First Approach
What It Is: The mobile-first approach in Tailwind CSS involves designing and building your layout
primarily for smaller screen sizes first, and then progressively enhancing it for larger screens.
How Tailwind Helps: Tailwind CSS supports this by default, with utilities applied to smaller
screens and scaled up using responsive breakpoints.
Example:
<div class="text-center sm:text-left lg:text-right">
<p>Responsive Text Alignment</p>
</div>
In this example, the text will be centered on mobile, aligned to the left on small screens (using
sm:), and aligned to the right on large screens (lg:).
2. Flexible Grid Layouts
What It Is: Flexible grid layouts allow you to structure content in rows and columns that adjust
based on the available screen size.
How Tailwind Helps: Tailwind provides utilities for creating responsive grid layouts using grid
and grid-cols classes, allowing you to define how many columns a layout should have at
various breakpoints.
By Chris H (Lycee APADE) Page 112
113 | P a g e
Example:
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<div class="p-4">Item 1</div>
<div class="p-4">Item 2</div>
<div class="p-4">Item 3</div>
</div>
Here, on small screens, the grid will have one column, on medium screens it will have two, and
on large screens, it will have three columns.
3. Responsive Images and Media
What It Is: Optimizing images and media to look good across all screen sizes and resolutions.
How Tailwind Helps: Tailwind provides responsive image utilities like max-w-full (to ensure
images don’t overflow the container) and object-cover (to maintain image aspect ratios).
Example:
<img src="image.jpg" class="w-full h-auto object-cover" alt="Responsive
image">
This ensures the image fills its container while maintaining its aspect ratio across all screen sizes.
4. Media Queries and Breakpoints
What It Is: Media queries are used to apply different styles based on device characteristics like
width, height, and resolution.
How Tailwind Helps: Tailwind CSS has predefined breakpoints (sm, md, lg, xl, 2xl) that allow
you to apply styles at specific screen sizes.
Example:
<div class="bg-blue-500 sm:bg-red-500 lg:bg-green-500">
<p>Responsive Background Color</p>
</div>
This applies different background colors based on the screen size, changing the color at the sm
and lg breakpoints.
5. Typography and Readability
What It Is: Ensuring text is legible and properly styled for various screen sizes, with
considerations for font sizes, line heights, spacing, and more.
How Tailwind Helps: Tailwind makes typography adjustments easier with utilities for controlling
font size, line height, text color, and more.
Example:
<p class="text-lg sm:text-xl md:text-2xl">
This text will resize based on screen size.
By Chris H (Lycee APADE) Page 113
114 | P a g e
</p>
6. Interactive Elements
What It Is: Making sure buttons, links, forms, and other interactive elements are accessible and
styled appropriately across devices.
How Tailwind Helps: Tailwind provides utilities to add hover, focus, and active states to
interactive elements, ensuring they work well across screen sizes.
Example:
<button class="bg-blue-500 hover:bg-blue-700 focus:outline-none px-4
py-2 text-white rounded">
Click Me
</button>
7. Testing and Iteration
What It Is: Continuously testing your design on different devices and iterating on your layout for
the best user experience.
How Tailwind Helps: Tailwind allows you to quickly make changes and see how your layout
adapts by using its utility-first approach. Additionally, tools like browser dev tools let you test
breakpoints easily.
Example: Testing layouts directly on different screen sizes by resizing the browser or using
device simulation modes.
Objective: Tailwind styles for a unique look are accurately customized based on tailwind configuration
● Customization of tailwind styles
1. Extending the Default Theme
What It Is: Tailwind CSS comes with a default theme that includes a set of predefined colors,
spacing, typography, and other utilities. You can extend this default theme to include custom
values specific to your project.
How to Customize: You can extend the default theme in your tailwind.config.js file. This
allows you to add your own custom settings without modifying the default ones.
Example:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
'custom-blue': '#1c92d2',
},
spacing: {
'128': '32rem',
By Chris H (Lycee APADE) Page 114
115 | P a g e
},
},
},
};
In this example, we added a custom blue color and a new spacing value (128 for 32rem) to the
theme.
2. Adding Custom Variants
What It Is: Variants in Tailwind CSS allow you to apply styles under specific states, such as
hover, focus, or active. You can also define custom variants to control when certain styles
are applied.
How to Customize: Tailwind allows you to add custom variants in the variants section of the
tailwind.config.js. For example, if you want to create a custom variant for when an
element is "even" in a list:
Example:
// tailwind.config.js
module.exports = {
variants: {
extend: {
backgroundColor: ['even'],
},
},
};
This would allow you to apply a background color to even elements in a list using the even:bg-
color class.
3. Custom Fonts and Typography
What It Is: You can customize the typography settings in Tailwind CSS, including font families,
sizes, and line heights, to match your design requirements.
How to Customize: To add custom fonts or modify typography settings, you need to extend the
theme and include the desired font family.
Example:
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Roboto', 'Arial', 'sans-serif'],
},
fontSize: {
'xxl': '1.75rem',
},
},
},
};
By Chris H (Lycee APADE) Page 115
116 | P a g e
This example sets the default sans-serif font to 'Roboto' and adds a custom xxl font size.
4. Customizing Colors
What It Is: Tailwind comes with a set of colors, but you can define your own custom colors to
match your brand or project requirements.
How to Customize: You can easily extend or overwrite the default color palette in the
tailwind.config.js file.
Example:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: '#ff5722',
accent: '#4caf50',
},
},
},
};
This will add brand and accent colors to your Tailwind utility classes (e.g., bg-brand, text-
accent).
5. Plugins for Additional Functionality
What It Is: Tailwind CSS has a plugin ecosystem that allows you to extend its functionality.
Plugins can introduce new utilities, components, or features to your project.
How to Use: You can use existing plugins or create your own custom plugins. Tailwind’s official
plugins include typography, forms, and aspect-ratio.
Example (Using a plugin):
npm install @tailwindcss/forms
Then, add the plugin to your tailwind.config.js:
// tailwind.config.js
module.exports = {
plugins: [
require('@tailwindcss/forms'),
],
};
This example adds the official @tailwindcss/forms plugin, which enhances form element
styling with minimal effort.
By Chris H (Lycee APADE) Page 116
117 | P a g e
6. Custom Directives for Complex Designs
What It Is: Tailwind CSS allows you to write custom directives in your tailwind.config.js to
handle more complex designs. You can define custom utilities, components, or even complex
CSS rules.
How to Use: You can create your own custom utilities or responsive breakpoints using the
addUtilities or addComponents functions within the @layer directive.
Example:
// tailwind.config.js
module.exports = {
plugins: [
function ({ addUtilities }) {
addUtilities({
'.skewed': {
transform: 'skewX(-20deg)',
},
}, ['responsive', 'hover']);
},
],
};
In this example, a .skewed class is added to the project, which applies a skew transform. It is
also responsive and works on hover.
7. Conditional Styles with JavaScript
What It Is: Sometimes, you may want to conditionally apply styles based on some JavaScript
logic (e.g., whether a user is logged in, or based on screen size). Tailwind allows you to use
JavaScript to dynamically add/remove classes.
How to Use: Use JavaScript to manipulate the class list of elements based on conditions.
Typically, this is done with vanilla JavaScript, but also with frameworks like React, Vue, or
Alpine.js.
Example (Using vanilla JavaScript):
<button id="themeButton" class="bg-blue-500 hover:bg-blue-700">
Toggle Theme
</button>
<script>
const button = document.getElementById('themeButton');
button.addEventListener('click', () => {
document.body.classList.toggle('dark');
});
</script>
In this example, clicking the button toggles the dark class on the body element, which could
trigger a set of dark mode styles defined in Tailwind.
By Chris H (Lycee APADE) Page 117
118 | P a g e
Summary
By customizing Tailwind CSS, you can tailor the framework to meet your exact design needs.
Whether it’s adding your own colors, fonts, or even creating complex layouts using custom
directives and plugins, Tailwind gives you the flexibility to extend its functionality in many
powerful ways. Additionally, integrating Tailwind with JavaScript allows for dynamic styling
that adapts to user interactions or other conditions.
Develop Next.JS Application ()
Objective: Typescript basics are properly applied based on project requirements.
1. Applying TypeScript basics
Environment Setup
To start using TypeScript, you need to set up your development environment.
Installing TypeScript
1. Install Node.js if it's not already installed. You can get it from here.
2. Install TypeScript globally using npm (Node Package Manager). Open your terminal
and run:
npm install -g typescript
3. Check installation by verifying the TypeScript version:
tsc --version
Configuring TypeScript
TypeScript uses a tsconfig.json file to configure the compiler options.
1. Create a tsconfig.json file in the root directory of your project by running:
tsc --init
2. The tsconfig.json file will contain configurations, such as:
By Chris H (Lycee APADE) Page 118
119 | P a g e
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist"
},
"include": [
"src/**/*.ts"
]
}
This file controls how TypeScript will transpile your code, such as what ECMAScript version to
output and where to place compiled JavaScript files.
2. Implementing Interface of Variables
Interfaces are used to define the structure of objects in TypeScript.
Example:
interface Person {
name: string;
age: number;
}
const person: Person = {
name: "John Doe",
age: 30
};
console.log(person);
interface Person specifies that any object of type Person must have a name (string) and
age (number).
The variable person conforms to this interface.
Interfaces can also be used to define the shape of function signatures or classes.
3. Handling Functions in TypeScript
TypeScript supports both regular functions and functions with type annotations for parameters
and return types.
Example:
function greet(name: string): string {
return `Hello, ${name}`;
}
By Chris H (Lycee APADE) Page 119
120 | P a g e
const result = greet("Alice");
console.log(result);
Here, name is of type string, and the function returns a string.
Arrow Function Example:
const add = (a: number, b: number): number => {
return a + b;
};
console.log(add(2, 3)); // Output: 5
Optional and Default Parameters:
function greet(name: string, age?: number): string {
return age ? `${name}, age ${age}` : `${name}, age unknown`;
}
console.log(greet("Alice"));
console.log(greet("Bob", 25));
age? makes the age parameter optional.
4. Data Handling
API Data Validation
When working with APIs, it's important to ensure that data returned by the API is in the expected
format.
Example:
interface ApiResponse {
status: string;
data: any;
}
function handleApiResponse(response: ApiResponse) {
if (response.status === "success") {
console.log("Data received:", response.data);
} else {
console.log("Error:", response.status);
}
}
const response: ApiResponse = { status: "success", data: { id: 1, name:
"John" } };
handleApiResponse(response);
ApiResponse is an interface that ensures the response contains a status string and data
field.
Form Validation
TypeScript can help with form validation by specifying types for form fields.
By Chris H (Lycee APADE) Page 120
121 | P a g e
Example:
interface FormData {
name: string;
email: string;
}
function validateForm(data: FormData): boolean {
if (!data.name || !data.email) {
return false;
}
return true;
}
const formData: FormData = { name: "John", email: "john@example.com" };
console.log(validateForm(formData)); // Output: true
FormData defines the structure of form data, and validateForm checks whether the name
and email fields are populated.
5. Error Handling and Exceptions
TypeScript also allows for handling errors and exceptions using try, catch, and finally.
Example:
function divide(a: number, b: number): number {
try {
if (b === 0) {
throw new Error("Division by zero is not allowed");
}
return a / b;
} catch (error) {
console.error(error.message);
return 0;
} finally {
console.log("Function executed");
}
}
console.log(divide(4, 2)); // Output: 2
console.log(divide(4, 0)); // Output: Error message and 0
try-catch is used to catch any runtime errors, such as division by zero.
The finally block ensures code execution regardless of the outcome.
By using these features of TypeScript, you can write clean, type-safe code that handles data
validation, function signatures, and error handling with ease.
By Chris H (Lycee APADE) Page 121
122 | P a g e
Objective: Routing is properly implemented based on components.
Setup NextJS project
1. Setup Next.js Project
Preparation of Environment
Before you can start working with Next.js, make sure you have the necessary tools installed on
your machine:
1. Install Node.js:
You’ll need Node.js and npm (Node Package Manager) to work with Next.js. Download
and install it from Node.js official website.
2. Install Yarn (optional):
While npm is the default package manager, some developers prefer Yarn. Install it with
the following command: npm install -g yarn
Project Creation
To create a new Next.js project, follow these steps:
1. Create a new Next.js app using create-next-app: You can create a Next.js project
using either npm or Yarn. Below is the npm command:
npx create-next-app@latest my-next-app
o Replace my-next-app with the desired project name.
Alternatively, if you're using Yarn:
yarn create next-app my-next-app
2. Navigate to the project directory:
cd my-next-app
3. Run the development server: Once the project is created, you can start the development
server:
npm run dev
or with Yarn:
yarn dev
By Chris H (Lycee APADE) Page 122
123 | P a g e
This will start a development server, and you can view the app by opening
http://localhost:3000 in your browser.
2. Initial Development
Creating Pages and Components
1. Pages in Next.js
In Next.js, pages are created by adding .js (or .ts for TypeScript) files to the pages/
directory. Each file represents a route in your application.
Example of creating a new page:
o Create a file pages/about.js:
// pages/about.js
export default function About() {
return <h1>About Page</h1>;
}
Now, visiting http://localhost:3000/about will show the "About Page".
2. Components in Next.js
Components in Next.js can be placed in a components/ directory. You can create
reusable components and import them into your pages.
Example of creating a component:
o Create a file components/Header.js:
// components/Header.js
export default function Header() {
return <header><h1>Welcome to My Website</h1></header>;
}
o Then import and use the component in your pages/index.js:
// pages/index.js
import Header from '../components/Header';
export default function Home() {
return (
<div>
<Header />
<p>Welcome to my Next.js app!</p>
</div>
);
}
By Chris H (Lycee APADE) Page 123
124 | P a g e
Implementing Search Engine Optimization (SEO)
Next.js provides several built-in features for SEO, such as automatic static optimization and
custom <head> tags using next/head.
1. Using next/head for SEO meta tags: To implement SEO, you should add meta tags
such as the title, description, and Open Graph tags.
Example of adding SEO meta tags in pages/index.js:
import Head from 'next/head';
export default function Home() {
return (
<div>
<Head>
<title>My Next.js Website</title>
<meta name="description" content="This is my Next.js website"
/>
<meta property="og:title" content="My Next.js Website" />
<meta property="og:description" content="This is my Next.js
website" />
</Head>
<h1>Welcome to My Website!</h1>
</div>
);
}
o next/head allows you to dynamically insert meta tags and titles for each page. This is
critical for SEO because it helps search engines index your pages appropriately.
2. Automatic Static Optimization:
Next.js automatically optimizes pages at build time if they don’t use server-side code,
providing fast loading times and better SEO.
Styling
Next.js supports multiple styling options, including built-in support for CSS, Sass, and CSS-in-
JS libraries such as styled-components or @emotion.
1. CSS and Sass:
o You can use global CSS by importing styles in pages/_app.js:
// pages/_app.js
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
By Chris H (Lycee APADE) Page 124
125 | P a g e
export default MyApp;
o For Sass, you need to install the Sass package:
bash
CopyEdit
npm install sass
Then you can use .scss or .sass files like so:
jsx
CopyEdit
// pages/_app.js
import '../styles/globals.scss';
2. CSS Modules:
Next.js also supports CSS Modules, which scope styles to specific components,
preventing styles from leaking globally.
Example of using CSS Modules:
o Create a CSS module file styles/Home.module.css:
css
CopyEdit
.title {
color: blue;
}
o Use the CSS module in a component:
jsx
CopyEdit
// pages/index.js
import styles from '../styles/Home.module.css';
export default function Home() {
return <h1 className={styles.title}>Welcome to My
Website!</h1>;
}
By Chris H (Lycee APADE) Page 125
126 | P a g e
Caching Strategies
Next.js has built-in caching strategies that improve performance, especially when deploying to
Vercel or other static hosting platforms.
1. Static Generation with getStaticProps:
Next.js pre-renders pages at build time, meaning the HTML is cached and served as static
files. This is perfect for pages that don’t require frequent updates.
Example:
// pages/index.js
export async function getStaticProps() {
return {
props: {
message: 'Hello from static generation!'
}
};
}
export default function Home({ message }) {
return <h1>{message}</h1>;
}
2. Incremental Static Regeneration (ISR):
ISR allows you to update static content after deployment, by re-building the page in the
background and caching the new version.
Example:
// pages/index.js
export async function getStaticProps() {
return {
props: { message: 'Hello from static generation with ISR!' },
revalidate: 10, // Revalidate every 10 seconds
};
}
o This means after 10 seconds, the page will be rebuilt in the background if there are any
new requests, ensuring that users get fresh content without rebuilding on every
request.
3. Client-side Caching:
For client-side caching, you can use service workers (PWA) or other caching strategies
(e.g., SWR or React Query) to cache API responses and resources locally in the browser.
Example of using SWR:
By Chris H (Lycee APADE) Page 126
127 | P a g e
npm install swr
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then((res) => res.json());
function DataFetching() {
const { data, error } = useSWR('/api/data', fetcher);
if (error) return <div>Error occurred</div>;
if (!data) return <div>Loading...</div>;
return <div>{data}</div>;
}
SWR provides caching and revalidation of fetched data for better performance.
Objective: API is properly created based on RESTful design principles.
1. Implementing Rendering Techniques in Next.js
Next.js provides several rendering strategies that allow developers to optimize performance
based on specific use cases. These strategies include Static Site Generation (SSG), Server-Side
Rendering (SSR), Incremental Static Regeneration (ISR), and Client-Side Rendering (CSR).
1.1 Static Site Generation (SSG)
Static Site Generation (SSG) is a rendering technique where the HTML of a page is generated at
build time, meaning it is pre-rendered and stored as static files. This is ideal for pages that don't
change often and allows for fast page loads.
By Chris H (Lycee APADE) Page 127
128 | P a g e
How to implement SSG:
Use getStaticProps in Next.js to fetch data at build time and pre-render the page.
Example:
// pages/index.js
export async function getStaticProps() {
const data = await fetch('https://api.example.com/posts')
.then((response) => response.json());
return {
props: {
posts: data, // Pass the fetched data as props to the page component
},
};
}
export default function Home({ posts }) {
return (
<div>
<h1>All Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
In this example, getStaticProps fetches data from an API at build time and renders the
page as static HTML, which can then be cached by CDNs for fast delivery.
Benefits of SSG:
o Fast performance since the page is pre-rendered.
o Ideal for marketing pages, blogs, product pages, etc., where the data doesn't change
frequently.
o
Drawbacks:
o Requires rebuilding the site to update content.
1.2 Server-Side Rendering (SSR)
Server-Side Rendering (SSR) is a rendering technique where the HTML of a page is generated
on each request by the server. This is ideal for pages with dynamic content that needs to be
rendered on every request (e.g., user dashboards, search results).
By Chris H (Lycee APADE) Page 128
129 | P a g e
How to implement SSR:
Use getServerSideProps to fetch data on the server for each request before rendering the
page.
Example:
// pages/dashboard.js
export async function getServerSideProps() {
const data = await fetch('https://api.example.com/user/data')
.then((response) => response.json());
return {
props: {
userData: data, // Pass the data as props to the page component
},
};
}
export default function Dashboard({ userData }) {
return (
<div>
<h1>User Dashboard</h1>
<pre>{JSON.stringify(userData, null, 2)}</pre>
</div>
);
}
In this example, getServerSideProps fetches data on each request from the server
before rendering the page.
Benefits of SSR:
o Data is always up-to-date, since it's rendered on every request.
o Great for user-specific data, real-time data, or dynamic content.
Drawbacks:
o Slower response time compared to static pages since data must be fetched and the page
generated on every request.
o Increased load on the server.
1.3 Incremental Static Regeneration (ISR)
Incremental Static Regeneration (ISR) allows you to update static pages after the site has been
deployed, without needing to rebuild the entire site. ISR enables the regeneration of static pages
in the background while serving the previously generated pages to users. This is useful for pages
with frequently changing content.
How to implement ISR:
Use getStaticProps along with the revalidate option to specify how often the page
should be re-generated.
By Chris H (Lycee APADE) Page 129
130 | P a g e
Example:
// pages/posts/[id].js
export async function getStaticPaths() {
const posts = await fetch('https://api.example.com/posts')
.then((response) => response.json());
const paths = posts.map((post) => ({
params: { id: post.id.toString() },
}));
return {
paths,
fallback: 'blocking', // or true/false based on your preference
};
}
export async function getStaticProps({ params }) {
const post = await fetch(`https://api.example.com/posts/${params.id}`)
.then((response) => response.json());
return {
props: {
post,
},
revalidate: 10, // Regenerate the page every 10 seconds
};
}
export default function Post({ post }) {
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
How ISR works:
o getStaticProps fetches data for static generation at build time.
o revalidate ensures that the page is re-generated in the background after a specified
amount of time (in this case, every 10 seconds).
o getStaticPaths is used for dynamic paths, such as blog posts with unique IDs.
Benefits of ISR:
o Combines the best of both SSG (fast delivery) and SSR (up-to-date content).
o Ideal for pages where content changes frequently, but full rebuilds are unnecessary.
Drawbacks:
o Somewhat more complex than SSG or SSR.
o Regeneration process can result in serving stale content until the page is rebuilt.
By Chris H (Lycee APADE) Page 130
131 | P a g e
1.4 Client-Side Rendering (CSR)
Client-Side Rendering (CSR) is the traditional approach where JavaScript runs in the browser to
dynamically generate HTML. In CSR, the initial HTML is just a shell (usually an empty <div>)
and content is fetched and rendered client-side.
How to implement CSR:
Use React hooks like useEffect to fetch data client-side.
Example:
// pages/products.js
import { useEffect, useState } from 'react';
export default function Products() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchProducts = async () => {
const response = await fetch('https://api.example.com/products');
const data = await response.json();
setProducts(data);
setLoading(false);
};
fetchProducts();
}, []);
if (loading) return <p>Loading...</p>;
return (
<div>
<h1>Product List</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
}
How CSR works:
o Initially, an empty shell is served from the server.
o Once the JavaScript loads in the browser, an API call is made (using fetch, for example)
to load data and render content client-side.
Benefits of CSR:
o Great for highly interactive pages and dynamic content.
By Chris H (Lycee APADE) Page 131
132 | P a g e
o Allows you to leverage the full power of JavaScript in the browser, including client-side
routing and state management.
Drawbacks:
o The initial page load can be slower because it depends on client-side JavaScript.
o SEO can be a challenge since the content is not pre-rendered.
Objective: Application is properly secured based on system requirements and security standards.
By Chris H (Lycee APADE) Page 132
133 | P a g e
This is a comprehensive breakdown of Next.js routing, API creation, and security practices.
1. Implementing Routing in Next.js
Next.js provides a file-system-based routing approach, meaning that files in the pages/
directory automatically become routes.
1.1 File-system Based Routing
Every .js or .tsx file inside the pages/ directory becomes a route automatically.
Example:
o pages/index.js → /
o pages/about.js → /about
o pages/contact.js → /contact
1.2 Dynamic Routes
Dynamic routes allow you to create flexible pages where parts of the URL are dynamic.
Example:
o pages/product/[id].js will match URLs like /product/1, /product/2, etc.
// pages/product/[id].js
import { useRouter } from 'next/router';
export default function ProductPage() {
const router = useRouter();
const { id } = router.query;
return <h1>Product ID: {id}</h1>;
}
The id parameter can be accessed via router.query.id.
1.3 Nested Routes
To create a nested route, simply create folders inside pages/.
Example:
pages/
blog/
index.js --> /blog
[id].js --> /blog/1
By Chris H (Lycee APADE) Page 133
134 | P a g e
Example Code (pages/blog/[id].js):
import { useRouter } from 'next/router';
export default function BlogPost() {
const { id } = useRouter().query;
return <h1>Blog Post ID: {id}</h1>;
}
1.4 Link Component
Instead of using <a> tags (which cause full page reloads), use the Next.js <Link> component for
client-side navigation.
import Link from 'next/link';
export default function Home() {
return (
<div>
<h1>Home Page</h1>
<Link href="/about">Go to About Page</Link>
</div>
);
}
1.5 Programmatic Navigation
Use the useRouter hook to navigate programmatically.
import { useRouter } from 'next/router';
export default function Dashboard() {
const router = useRouter();
return (
<button onClick={() => router.push('/profile')}>
Go to Profile
</button>
);
}
1.6 API Routes
Next.js allows creating API endpoints inside the pages/api/ directory.
// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, API!' });
}
By Chris H (Lycee APADE) Page 134
135 | P a g e
Accessible at http://localhost:3000/api/hello.
1.7 Catch-all Routes
To match multiple segments in a route, use [...slug].js.
Example:
// pages/blog/[...slug].js
import { useRouter } from 'next/router';
export default function Blog() {
const router = useRouter();
const { slug } = router.query;
return <h1>Blog Slug: {slug?.join('/')}</h1>;
}
Matches /blog/a, /blog/a/b, /blog/a/b/c.
2. Creating an API in Next.js
2.1 Defining API Endpoints
API routes reside inside pages/api/.
Example:
// pages/api/user.js
export default function handler(req, res) {
res.status(200).json({ user: 'John Doe' });
}
2.2 Handling Request Types
Handle GET, POST, PUT, and DELETE methods.
// pages/api/users.js
export default function handler(req, res) {
if (req.method === 'GET') {
res.status(200).json({ message: 'GET request received' });
} else if (req.method === 'POST') {
res.status(201).json({ message: 'POST request received' });
} else {
By Chris H (Lycee APADE) Page 135
136 | P a g e
res.status(405).json({ message: 'Method Not Allowed' });
}
}
2.3 Using Dynamic API Routes
Create pages/api/user/[id].js for fetching users dynamically.
// pages/api/user/[id].js
export default function handler(req, res) {
const { id } = req.query;
res.status(200).json({ userId: id });
}
2.4 Testing Your API
You can test APIs using:
o Postman
o Curl
o Browser (http://localhost:3000/api/user/1)
3. Securing the Application
3.1 Performing Client-Side Security
3.1.1 CSR Security
Use environment variables for sensitive client-side information.
const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
3.1.2 Cross-Origin Resource Sharing (CORS)
Implement CORS to restrict API access.
// pages/api/hello.js
export default function handler(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.status(200).json({ message: 'CORS Enabled' });
}
By Chris H (Lycee APADE) Page 136
137 | P a g e
3.1.3 Session Management
Use next-auth for session-based authentication.
npm install next-auth
3.1.4 Third-party Authentication (Auth0)
Install:
npm install @auth0/nextjs-auth0
Implement Auth0 authentication.
3.2 Performing Server-Side Security
3.2.1 HTTPS Enforcement
Force HTTPS using .htaccess (for Apache) or next.config.js.
3.2.2 SSR Security
Use getServerSideProps carefully.
Avoid exposing sensitive data in client-side responses.
export async function getServerSideProps() {
return {
props: { secretData: process.env.SECRET_KEY },
};
}
3.2.3 API Routes Security
Restrict unauthorized access to API routes.
export default function handler(req, res) {
const apiKey = req.headers['x-api-key'];
if (apiKey !== process.env.API_KEY) {
return res.status(401).json({ message: 'Unauthorized' });
}
res.status(200).json({ message: 'Authorized' });
}
By Chris H (Lycee APADE) Page 137
138 | P a g e
3.2.4 Content Security Policy (CSP)
Implement CSP headers in next.config.js.
module.exports = {
headers: async () => [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-inline'",
},
],
},
],
};
3.2.5 Authentication
Implement JWT-based authentication.
npm install jsonwebtoken
javascript
CopyEdit
import jwt from 'jsonwebtoken';
export default function handler(req, res) {
const token = jwt.sign({ user: 'John Doe' }, process.env.JWT_SECRET, {
expiresIn: '1h',
});
res.status(200).json({ token });
}
3.2.6 General Security Measures
Use Helmet to set security headers.
npm install helmet
import helmet from 'helmet';
export default function handler(req, res) {
By Chris H (Lycee APADE) Page 138
139 | P a g e
res.setHeader('Content-Security-Policy', "default-src 'self'");
res.status(200).json({ message: 'Security headers applied' });
}
Conclusion
By implementing proper routing, API handling, and security measures, you can build a robust
and secure Next.js application that is optimized for performance and scalability. ✅
Apply Progressive Web Application ()
Applying Progressive Web Application (PWA) in Next.js �
A Progressive Web Application (PWA) is a web app that delivers a native app-like
experience through modern web capabilities such as offline support, push notifications, and
installability.
1. Benefits of PWAs in Next.js
✅Offline Support (via Service Workers)
✅Fast Loading (through caching & optimization)
✅Installable (can be added to the home screen like a native app)
✅Enhanced Security (served over HTTPS)
✅Better Performance (improves user experience & engagement)
2. Setting Up PWA in Next.js
To enable PWA in Next.js, we need to install a service worker and configure our
manifest.json file.
Step 1: Install Required Dependencies
Run the following command to install the next-pwa package:
npm install next-pwa
Step 2: Configure next-pwa in next.config.js
Modify your next.config.js file to enable PWA support.
const withPWA = require('next-pwa')({
dest: 'public', // Service worker will be generated in the public directory
By Chris H (Lycee APADE) Page 139
140 | P a g e
register: true, // Auto-register the service worker
skipWaiting: true, // Immediately activate the new service worker
});
module.exports = withPWA({
reactStrictMode: true,
});
Step 3: Create manifest.json
Inside the public/ directory, create a manifest.json file.
{
"name": "My Next.js PWA",
"short_name": "NextPWA",
"description": "A progressive web app built with Next.js",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
✅Ensure you have the icons in the public/icons/ folder.
Step 4: Generate Icons
You can create PWA icons using:
RealFaviconGenerator
Favicon.io
Place them inside public/icons/.
By Chris H (Lycee APADE) Page 140
141 | P a g e
Step 5: Implement Service Worker
Next.js handles this automatically with next-pwa, but you can verify by checking
public/service-worker.js.
Step 6: Deploy & Test PWA
After deploying your app, test the PWA features:
1. Run the Next.js app locally:
npm run build && npm start
2. Open Chrome DevTools (F12 → Application Tab → Manifest & Service Worker).
3. Check Installability: Click "Install" on the address bar or add it manually.
4. Test Offline Mode:
o Open DevTools → Network → Check "Offline."
o Reload the page to ensure it works offline.
Bonus: Enable Push Notifications
You can integrate push notifications with Firebase Cloud Messaging (FCM) for real-time
updates.
Objective: Responsiveness is correctly maintained in line with user requirements.
Maintain Responsiveness
1. Maintain Responsiveness
Responsiveness ensures your UI adapts to different screen sizes.
✅Use CSS Frameworks & Utility Classes
Tailwind CSS
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="p-4 bg-gray-200">Item 1</div>
<div className="p-4 bg-gray-300">Item 2</div>
<div className="p-4 bg-gray-400">Item 3</div>
By Chris H (Lycee APADE) Page 141
142 | P a g e
</div>
Styled Components (if using CSS-in-JS)
import styled from "styled-components";
const ResponsiveDiv = styled.div`
display: flex;
flex-direction: column;
@media (min-width: 768px) {
flex-direction: row;
}
`;
const Component = () => <ResponsiveDiv>Content</ResponsiveDiv>;
✅Use Next.js Image Optimization
import Image from 'next/image';
<Image
src="/image.jpg"
width={500}
height={300}
alt="Example"
className="w-full h-auto"
/>
2. Leverage Progressive Enhancement
Ensure core functionality works on all browsers and devices.
✅Use Semantic HTML
<header>
<h1>Welcome</h1>
</header>
<main>
<article>
<p>Content here...</p>
</article>
</main>
✅Gracefully Degrade Features
Use Next.js dynamic imports with SSR disabled for heavy components:
import dynamic from 'next/dynamic';
const HeavyComponent = dynamic(() => import('../components/HeavyComponent'),
{
ssr: false,
By Chris H (Lycee APADE) Page 142
143 | P a g e
});
export default function Page() {
return <HeavyComponent />;
}
✅Ensure JavaScript is Optional
Ensure navigation and forms work without JavaScript:
<a href="/contact">Contact Us</a>
<form method="POST" action="/submit">
<input type="text" name="name" required />
<button type="submit">Submit</button>
</form>
3. Prioritize Mobile-First Design
Design for small screens first, then scale up.
✅CSS Mobile-First Approach
.button {
padding: 8px 16px; /* Default for mobile */
}
@media (min-width: 768px) {
.button {
padding: 12px 24px; /* Larger for tablets and desktops */
}
}
✅Viewport Meta Tag in _app.js or _document.js
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
✅Use Flexbox/Grid for Layouts
<div className="flex flex-col md:flex-row">
<div className="w-full md:w-1/2">Left</div>
<div className="w-full md:w-1/2">Right</div>
</div>
4. Utilize Performance Optimization Techniques
Optimize loading speed and UX.
By Chris H (Lycee APADE) Page 143
144 | P a g e
✅Optimize Images (Use Next.js Image Component)
<Image src="/img.jpg" width={800} height={600} quality={80} priority
alt="Optimized Image" />
✅Lazy Load Components
const LazyComponent = dynamic(() => import("../components/LazyComponent"), {
ssr: false,
loading: () => <p>Loading...</p>,
});
✅Enable Automatic Static Optimization
Use getStaticProps for static generation.
Use getServerSideProps only when necessary.
✅Use next/script for Third-Party Scripts
import Script from 'next/script';
<Script
src="https://example.com/script.js"
strategy="lazyOnload"
/>
✅Optimize Fonts
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
export default function Page() {
return <p className={inter.className}>Hello</p>;
}
Objective: Web app manifest for an installable app experience is properly configured based on user
requirements.
By Chris H (Lycee APADE) Page 144
145 | P a g e
Configuring web application manifest
The Web App Manifest enables Progressive Web App (PWA) capabilities, allowing users to
install your app on their devices with a native-like experience.
1 Creating and Configuring the Manifest File
The manifest file is a JSON file (manifest.json) that provides metadata about your app.
Example public/manifest.json
Create a file at public/manifest.json with the following structure:
{
"name": "My Next.js App",
"short_name": "NextApp",
"description": "A Progressive Web App built with Next.js",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
✅Key Properties Explained:
name / short_name → App name for installation
By Chris H (Lycee APADE) Page 145
146 | P a g e
start_url → The URL the app opens when launched
display → Determines how the app is shown (standalone, fullscreen, etc.)
theme_color / background_color → Used for UI styling
icons → App icons for different screen sizes
2 Referencing the Manifest in Your HTML
Next.js uses the _document.js or _document.tsx file for custom HTML modifications.
Modify _document.js or _document.tsx
Add a <link> reference to manifest.json inside <Head>:
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html lang="en">
<Head>
<link rel="manifest" href="/manifest.json" />
<link rel="icon" href="/favicon.ico" />
<meta name="theme-color" content="#000000" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
✅Why?
This ensures the browser recognizes the manifest when visiting the app.
3 Testing and Validation
After setup, verify the manifest is working correctly.
�Check in Chrome DevTools
1. Open Chrome.
2. Right-click your page and select Inspect.
3. Go to the Application tab.
4. Under Manifest, confirm all properties are correctly loaded.
By Chris H (Lycee APADE) Page 146
147 | P a g e
�Test PWA Installability
1. Ensure your app is served over HTTPS (except for localhost).
2. Add a service worker (if needed for PWA features).
3. Open DevTools → Lighthouse → Run a PWA audit.
�Validate with an Online Tool
Use Web Manifest Validator to check for issues.
Bonus: Automate Manifest Handling
For easier manifest handling, use next-pwa:
1 Install it:
npm install next-pwa
2 Configure next.config.js:
const withPWA = require('next-pwa')({
dest: 'public',
register: true,
skipWaiting: true,
});
module.exports = withPWA({
reactStrictMode: true,
});
Objective: Service workers for caching offline capabilities are correctly implemented based on user’s
requirements.
Implementation of service workers
Service workers enable Progressive Web App (PWA) capabilities such as offline support,
caching, and background sync in Next.js.
By Chris H (Lycee APADE) Page 147
148 | P a g e
1 What are Service Workers?
A service worker is a JavaScript script that runs in the background, separate from the main
browser thread. It:
Intercepts network requests.
Caches assets for offline support.
Enables background synchronization and push notifications.
2 Registering and Installing a Service Worker
Step 1: Create a Service Worker File
Inside the public folder, create a file: public/sw.js.
const CACHE_NAME = "next-pwa-cache-v1";
const urlsToCache = ["/", "/offline", "/favicon.ico"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
});
✅What this does:
✔ Caches essential assets on install
✔ Intercepts network requests and serves cached responses
✔ Deletes old caches when a new service worker is activated
By Chris H (Lycee APADE) Page 148
149 | P a g e
Step 2: Register the Service Worker in Next.js
In pages/_app.js or pages/_app.tsx, register the service worker:
import { useEffect } from "react";
function MyApp({ Component, pageProps }) {
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/sw.js")
.then((registration) => {
console.log("Service Worker registered with scope:",
registration.scope);
})
.catch((error) => {
console.error("Service Worker registration failed:", error);
});
}
}, []);
return <Component {...pageProps} />;
}
export default MyApp;
✅What this does:
✔ Registers sw.js when the app loads
✔ Logs success or failure in the console
3 Caching Strategy Implementation
Caching strategies determine how and when to use cached data. Here are some common ones:
1 Cache First (Fastest, Good for Static Assets)
Use for: CSS, JS, fonts, images
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
return cachedResponse || fetch(event.request);
})
);
});
By Chris H (Lycee APADE) Page 149
150 | P a g e
2 Network First (Ensures Fresh Data)
Use for: API calls, dynamic content
self.addEventListener("fetch", (event) => {
event.respondWith(
fetch(event.request)
.then((response) => {
return response;
})
.catch(() => caches.match(event.request))
);
});
3 Stale-While-Revalidate (Hybrid)
Use for: Fast loading while updating in the background
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((cachedResponse) => {
const fetchPromise = fetch(event.request).then((networkResponse) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchPromise;
});
})
);
});
4 Updating the Service Worker
Service workers don’t update automatically. When changes are made, they must be replaced.
How to Update a Service Worker?
Modify sw.js and increment the cache version:
const CACHE_NAME = "next-pwa-cache-v2"; // Increment version number
Then, modify the activation event to remove old caches:
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
By Chris H (Lycee APADE) Page 150
151 | P a g e
);
})
);
});
Forcing an Update
When a new service worker is available:
1. Open Chrome DevTools → Application → Service Workers.
2. Click Update or Unregister → Refresh page.
Alternatively, add a prompt in your app to reload the page:
if (registration.waiting) {
registration.waiting.postMessage({ type: "SKIP_WAITING" });
window.location.reload();
}
� Summary
✅ Service workers enable offline caching, speed boosts, and PWA features.
✅ Create and register a service worker (sw.js).
✅ Implement caching strategies for better performance.
✅ Ensure updates by handling cache versioning.
Automating with next-pwa
For a hassle-free setup, use next-pwa:
1. Install the package:
npm install next-pwa
2. Modify next.config.js:
const withPWA = require("next-pwa")({
dest: "public",
register: true,
skipWaiting: true,
});
module.exports = withPWA({
reactStrictMode: true,
});
By Chris H (Lycee APADE) Page 151
152 | P a g e
This will automatically generate a service worker with optimal caching strategies!
Publish the application
Objective: Environment variables are correctly setup based on deployment environments.
Configuration of Environment Variables in Next.js �
Next.js supports environment variables for storing API keys, database URLs, FTP
credentials, and other sensitive data securely. Here's how to configure them properly.
1. Setting Environment Variables
You can define environment variables inside a .env file in your project root.
Create .env.local (For Local Development)
# Backend API
NEXT_PUBLIC_API_URL=https://api.example.com
# FTP Configuration
FTP_HOST=ftp.example.com
FTP_USER=myftpuser
FTP_PASS=myftppassword
# Storage Environment
STORAGE_PATH=/uploads
✅Prefix NEXT_PUBLIC_ is required for client-side access (e.g., NEXT_PUBLIC_API_URL).
✅Variables without the prefix are only available on the server.
2. Accessing Environment Variables in Next.js
In Server-side Code (getServerSideProps, API Routes, etc.)
export async function getServerSideProps() {
const apiUrl = process.env.API_URL;
const res = await fetch(`${apiUrl}/data`);
const data = await res.json();
return { props: { data } };
}
By Chris H (Lycee APADE) Page 152
153 | P a g e
process.env.VARIABLE_NAME is used to access environment variables.
In Client-side Code (Components, Hooks)
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
export default function Home() {
return <p>API URL: {apiUrl}</p>;
}
Only NEXT_PUBLIC_* variables are accessible in the browser.
3 Setting Up Storage Environment
If your app interacts with cloud storage (AWS S3, Google Cloud, etc.), you can store credentials
in .env.local:
STORAGE_PROVIDER=aws
AWS_ACCESS_KEY=YOUR_ACCESS_KEY
AWS_SECRET_KEY=YOUR_SECRET_KEY
AWS_BUCKET_NAME=my-bucket
Use the Variables in Your Code
const storageProvider = process.env.STORAGE_PROVIDER;
if (storageProvider === "aws") {
console.log("Using AWS for storage");
}
4 Platform-Specific Configuration
Each platform handles environment variables differently:
Vercel (Next.js Hosting)
1 Set environment variables in the Vercel dashboard
Go to Vercel Dashboard → Your Project → Settings → Environment Variables
2✅⃣ Deploy, and Next.js will automatically pick them up!
Docker (For Containerized Deployments)
Create a .env file and load it in docker-compose.yml:
version: '3'
By Chris H (Lycee APADE) Page 153
154 | P a g e
services:
app:
image: my-next-app
env_file:
- .env
Netlify
In Netlify Dashboard, go to Site Settings → Environment → Add your variables.
5 Separate .env Files for Different Environments
You can use multiple .env files to manage different environments:
Files:
.env.local # For local development
.env.development # For development mode (NEXT_DEV)
.env.production # For production mode (NEXT_PROD)
Example .env.production
NEXT_PUBLIC_API_URL=https://api.production.com
✅Next.js automatically loads .env.local but ignores it in production.
6 Best Practices
✔ Never commit .env files – Add .env* to .gitignore
✔ Use NEXT_PUBLIC_ prefix for client-side access
✔ Define platform-specific variables (Vercel, Netlify, Docker)
✔ Separate configurations per environment (.env.local, .env.production)
�Summary
Use .env.local for local secrets.
Access variables via process.env (client-side needs NEXT_PUBLIC_).
Store secrets securely in Vercel, Netlify, or Docker configs.
Separate .env files for different environments.
By Chris H (Lycee APADE) Page 154
155 | P a g e
Objective: React application is properly deployed based on platform requirements.
Deploying React Application
Deploying a React Application (Next.js) to Vercel
Vercel is the best deployment platform for React and Next.js apps. It offers seamless
integration, automatic builds, and global CDN support for fast performance.
1 Run the Build Script in a React (Next.js) Application
Before deploying, ensure your Next.js app is production-ready by running:
npm run build
or
yarn build
What Happens?
✔ Compiles your project
✔ Optimizes static files
✔ Generates .next directory for production use
If the build fails, check for errors in your terminal and fix them before proceeding.
2 Configure Deployment on Vercel
Option 1: Deploy with Vercel CLI
1 Install Vercel CLI (if not installed)
npm install -g vercel
2 Log in to Vercel
vercel login
3 Deploy the Project
Run:
By Chris H (Lycee APADE) Page 155
156 | P a g e
vercel
Follow the on-screen prompts:
Select your Vercel project (or create a new one).
Choose the default settings.
Wait for deployment to finish.
✅Vercel auto-detects Next.js and optimizes the deployment.
Option 2: Deploy via Vercel Dashboard
1. Go to Vercel Dashboard
2. Click "New Project"
3. Connect your GitHub, GitLab, or Bitbucket repository
4. Select your React/Next.js project
5. Click "Deploy"
Vercel will automatically:
✔ Clone your repository
✔ Install dependencies
✔ Build the project
✔ Deploy it on a custom domain (your-app.vercel.app)
3 Migrate the Application Files
If moving from another platform:
Ensure all dependencies are installed (npm install).
Check .env variables (if any).
Remove unnecessary files to reduce deployment size.
By Chris H (Lycee APADE) Page 156
157 | P a g e
4 Test the Deployed Application
Check the Live Site
Open your Vercel project dashboard
Click on the deployed link (your-app.vercel.app)
Verify that everything works correctly
Run Lighthouse Performance Audit
Open Chrome DevTools → Lighthouse
Run a Performance & PWA test
Check Logs for Errors
Use Vercel Logs to debug issues:
vercel logs
Objective: Custom domain is appropriately Set up based on deployed application.
Setup custom Domain
Setting Up a Custom Domain for Your React (Next.js) App
A custom domain (e.g., yourdomain.com) gives your website a professional identity instead of
using your-app.vercel.app. This guide explains how to configure a domain, set up DNS and
SSL, and verify deployment.
1. Understanding DNS (Domain Name System)
DNS is like the phonebook of the internet, converting human-friendly domain names into
machine-friendly IP addresses.
By Chris H (Lycee APADE) Page 157
158 | P a g e
Key Concepts
Translation of Domain Names to IP Addresses → When you enter example.com, DNS
translates it into an IP like 192.0.2.1.
Hierarchy and Structure → DNS is structured in a hierarchy:
Root Servers (.) → Manage Top-Level Domains (TLDs)
TLD Servers (.com, .org, .net) → Handle domain extensions
Authoritative DNS Servers → Store specific domain records
✅Name Resolution Process
Your browser checks the cache.
If not found, it queries a recursive DNS server.
The recursive server finds the authoritative DNS server for the domain.
The IP address is returned to the browser.
2. Configure DNS and SSL Settings
Step 1: Buy a Custom Domain
Purchase a domain from a registrar like:
o Namecheap
o Google Domains
o GoDaddy
Step 2: Connect the Domain to Vercel
1. Go to the Vercel Dashboard
2. Select your project
3. Navigate to Settings → Domains
4. Click "Add Domain"
5. Enter your custom domain (yourdomain.com)
Step 3: Configure DNS Records
Once you add your domain in Vercel, you'll receive DNS records to configure in your domain
registrar.
For Root Domain (yourdomain.com)
o Type: A Record
By Chris H (Lycee APADE) Page 158
159 | P a g e
o Host: @
o Value: Vercel’s provided IP (e.g., 76.76.21.21)
For Subdomain (www.yourdomain.com)
o Type: CNAME
o Host: www
o Value: cname.vercel-dns.com
Add these records in your domain registrar’s DNS settings.
Step 4: Enable SSL (HTTPS)
1. Go back to Vercel Dashboard → Domains
2. Click "Enable SSL"
3. Vercel will automatically generate a free SSL certificate (via Let’s Encrypt).
SSL ensures secure HTTPS connections for your website.
3. Testing and Verification
Step 1: Check if DNS Has Propagated
DNS changes may take a few minutes to 48 hours to fully update.
You can check using:
DNS Checker → Verify your domain resolves correctly.
Step 2: Verify HTTPS is Working
Open your browser and visit https://yourdomain.com.
Ensure the �lock icon appears, indicating SSL is active.
Step 3: Run a Curl Command (Optional)
Test your domain using a terminal:
curl -I https://yourdomain.com
By Chris H (Lycee APADE) Page 159
160 | P a g e
If SSL is enabled, the response should include:
makefile
HTTP/2 200
server: Vercel
Summary
✅DNS translates domain names into IP addresses.
✅Configure A and CNAME records to point to Vercel.
✅Enable SSL for HTTPS security.
✅Test with a browser, DNS checker, and Curl.
By Chris H (Lycee APADE) Page 160