JavaScript and React course Β· Module 8: Advanced React Patterns
Performance Profiling
In this lesson7
Our cosmic journey through React is nearing its end, but before we set off for further explorations, we need to make sure our spaceship is running at maximum capacity. In this module, we will focus on profiling the performance of React applications - that is, identifying and analyzing "bottlenecks" in our code.
What is performance profiling?
Performance profiling is the process of identifying places in an application that consume the most resources (CPU time, memory) and may lead to slow user interface behavior. It is like diagnosing the engines of a spaceship to check which parts are not running efficiently and can be optimized.
In the context of React applications, profiling allows you to:
- Identify components that render too often
- Locate long-running operations
- Understand rendering patterns
- Detect unnecessary state updates
- Point out areas requiring optimization
React profiling tools
1. React DevTools Profiler
React DevTools is a browser extension that allows analyzing the performance of React applications. It includes a "Profiler" tab, which is the simplest and most powerful tool for profiling React applications.
How to use React DevTools Profiler:
Installing React DevTools
- For Chrome: React Developer Tools
- For Firefox: React Developer Tools
Starting profiling:
- Open browser developer tools
- Go to the "Profiler" tab in React DevTools
- Click the record button (red circle)
- Perform actions in the application you want to profile
- Stop recording
Analyzing results:
- Flame Chart - shows the hierarchy of rendered components and their rendering time
- Ranked Chart - sorts components by rendering time
- Component Chart - shows which components were re-rendered in each update
Interpreting colors in the Profiler:
- Gray - the component did not render during the update
- Yellow/Orange/Red - the component rendered, and the color indicates how long it took (the more red, the longer)
- Black - the component was the cause of the update (e.g., by changing state)
2. Chrome Performance Panel
Chrome DevTools includes a Performance panel that allows for more general page performance profiling:
Starting profiling:
- Open DevTools in Chrome (F12)
- Go to the "Performance" tab
- Click the record button
- Perform actions in the application
- Stop recording
Analyze results:
- Main - shows activity on the main thread (where most JS work happens)
- Frames - shows frames per second (FPS)
- Network - shows network activity
- Memory - shows memory usage
This panel is particularly useful for identifying:
- Long-running scripts blocking the main thread
- Excessive DOM operations
- Rendering performance issues
3. Built-in React profiling API
React provides a built-in API for profiling in development mode:
1// Import the Profiler component
2import { Profiler } from 'react';
3
4// Callback function called after rendering
5function onRenderCallback(
6 id, // identifier of the profiled component tree
7 phase, // "mount" (first render) or "update" (re-render)
8 actualDuration, // time spent rendering
9 baseDuration, // estimated render time of the entire subtree without optimizations
10 startTime, // when React started rendering
11 commitTime, // when React finished rendering
12 interactions // set of "interactions" tracked for this render
13) {
14 console.log({
15 id,
16 phase,
17 actualDuration,
18 baseDuration,
19 startTime,
20 commitTime
21 });
22}
23
24// Using the Profiler component in the application
25function App() {
26 return (
27 <Profiler id="MissionControl" onRender={onRenderCallback}>
28 <MissionControl />
29 </Profiler>
30 );
31}You can nest Profiler components to profile different parts of the application:
1function Dashboard() {
2 return (
3 <Profiler id="Dashboard" onRender={onRenderCallback}>
4 <h1>Mission Control Panel</h1>
5
6 <Profiler id="Navigation" onRender={onRenderCallback}>
7 <Navigation />
8 </Profiler>
9
10 <Profiler id="Stats" onRender={onRenderCallback}>
11 <MissionStats />
12 </Profiler>
13
14 <Profiler id="Crew" onRender={onRenderCallback}>
15 <CrewStatus />
16 </Profiler>
17 </Profiler>
18 );
19}This method of profiling is particularly useful when:
- You want to collect performance data in a test environment
- You have specific components you want to monitor
- You want to automate performance data collection
Common performance issues and how to profile them
1. Components rendering too frequently
Problem: Components re-render even when their data hasn't changed.
How to profile:
- Use React DevTools Profiler to see which components render too often
- Check if parent components are causing unnecessary child renders
Example of the problem:
1function ParentComponent() {
2 const [count, setCount] = useState(0);
3
4 // This function is created anew on every render
5 const handleClick = () => {
6 console.log('Button clicked');
7 };
8
9 return (
10 <div>
11 <button onClick={() => setCount(count + 1)}>
12 Increase counter: {count}
13 </button>
14
15 {/* ChildComponent will re-render on every count change,
16 even though handleClick has the same functionality */}
17 <ChildComponent onClick={handleClick} />
18 </div>
19 );
20}Solution:
1function ParentComponent() {
2 const [count, setCount] = useState(0);
3
4 // Function memoization prevents creating a new reference on every render
5 const handleClick = useCallback(() => {
6 console.log('Button clicked');
7 }, []);
8
9 return (
10 <div>
11 <button onClick={() => setCount(count + 1)}>
12 Increase counter: {count}
13 </button>
14
15 {/* ChildComponent wrapped in React.memo won't re-render
16 when its props don't change */}
17 <MemoizedChildComponent onClick={handleClick} />
18 </div>
19 );
20}
21
22const MemoizedChildComponent = React.memo(ChildComponent);2. Heavy computations during rendering
Problem: Performing complex calculations during rendering causes UI delays.
How to profile:
- Observe in React DevTools Profiler which components have the longest rendering time
- Use Chrome Performance Panel to see long JavaScript tasks
Example of the problem:
1function DataAnalytics({ data }) {
2 // Heavy computations performed on every render
3 const processedData = processLargeDataset(data);
4 const statistics = calculateStatistics(processedData);
5
6 return (
7 <div>
8 <h2>Mission Data Analysis</h2>
9 <DataVisualisation stats={statistics} />
10 </div>
11 );
12}Solution:
1function DataAnalytics({ data }) {
2 // Memoizing heavy computations - they only run when data changes
3 const processedData = useMemo(() => {
4 return processLargeDataset(data);
5 }, [data]);
6
7 const statistics = useMemo(() => {
8 return calculateStatistics(processedData);
9 }, [processedData]);
10
11 return (
12 <div>
13 <h2>Mission Data Analysis</h2>
14 <DataVisualisation stats={statistics} />
15 </div>
16 );
17}3. Excessive state updates
Problem: Too frequent state updates cause cascading re-renders.
How to profile:
- In React DevTools Profiler, find components marked in black (update initiators)
- Observe update patterns - are some components updating in groups?
Example of the problem:
1function SpaceshipSensors() {
2 const [temperature, setTemperature] = useState(0);
3 const [pressure, setPressure] = useState(0);
4 const [oxygen, setOxygen] = useState(0);
5 const [radiation, setRadiation] = useState(0);
6
7 // Fetching sensor data every 100ms
8 useEffect(() => {
9 const interval = setInterval(() => {
10 // Each update causes a separate render
11 setTemperature(getTemperature());
12 setPressure(getPressure());
13 setOxygen(getOxygen());
14 setRadiation(getRadiation());
15 }, 100);
16
17 return () => clearInterval(interval);
18 }, []);
19
20 return (
21 <div>
22 <h2>Sensor Readings</h2>
23 <p>Temperature: {temperature}Β°C</p>
24 <p>Pressure: {pressure} hPa</p>
25 <p>Oxygen level: {oxygen}%</p>
26 <p>Radiation level: {radiation} ΞΌSv/h</p>
27 </div>
28 );
29}Solution:
1function SpaceshipSensors() {
2 // Storing related data together in a single state object
3 const [sensorData, setSensorData] = useState({
4 temperature: 0,
5 pressure: 0,
6 oxygen: 0,
7 radiation: 0
8 });
9
10 useEffect(() => {
11 const interval = setInterval(() => {
12 // One state update instead of four
13 setSensorData({
14 temperature: getTemperature(),
15 pressure: getPressure(),
16 oxygen: getOxygen(),
17 radiation: getRadiation()
18 });
19 }, 100);
20
21 return () => clearInterval(interval);
22 }, []);
23
24 return (
25 <div>
26 <h2>Sensor Readings</h2>
27 <p>Temperature: {sensorData.temperature}Β°C</p>
28 <p>Pressure: {sensorData.pressure} hPa</p>
29 <p>Oxygen level: {sensorData.oxygen}%</p>
30 <p>Radiation level: {sensorData.radiation} ΞΌSv/h</p>
31 </div>
32 );
33}4. Large lists and data visualizations
Problem: Rendering long lists or complex visualizations can slow down the interface.
How to profile:
- Use React DevTools Profiler to identify list components that take a long time to render
- Check how often individual list items render
Example of using React DevTools to profile list performance:
- Start profiling
- Scroll the list to see how the application responds
- Stop profiling
- Check which components have the longest rendering time in the ranked chart
Solution:
- List virtualization (React Window, React Virtualized)
- Memoizing list items
- Pagination instead of displaying all elements at once
Advanced profiling techniques
1. User Timing API
The Browser Performance API allows creating custom time markers:
1function ExpensiveComponent({ data }) {
2 useEffect(() => {
3 // Start measurement
4 performance.mark('expensive-calculation-start');
5
6 // Perform expensive calculations
7 const result = performExpensiveCalculation(data);
8
9 // End measurement
10 performance.mark('expensive-calculation-end');
11
12 // Create measurement between markers
13 performance.measure(
14 'expensive-calculation',
15 'expensive-calculation-start',
16 'expensive-calculation-end'
17 );
18
19 // Log results
20 console.log(performance.getEntriesByName('expensive-calculation'));
21 }, [data]);
22
23 //
24}These markers will be visible in the Performance panel in Chrome DevTools.
2. Production performance monitoring
For monitoring performance in production, you can use:
- Web Vitals - a Google library for measuring key performance indicators
- New Relic, Datadog, Sentry - APM (Application Performance Monitoring) tools
- Custom solutions using the Performance API and Profiler API
Example of collecting data from React Profiler and sending it to a server:
1function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
2 // We only collect data from the update phase that takes longer than 10ms
3 if (phase === 'update' && actualDuration > 10) {
4 sendToAnalyticsServer({
5 componentId: id,
6 renderTime: actualDuration,
7 timestamp: Date.now()
8 });
9 }
10}
11
12function App() {
13 return (
14 <Profiler id="App" onRender={onRenderCallback}>
15 <AppContent />
16 </Profiler>
17 );
18}Case study: Profiling and optimizing a React application
Let's analyze the process of profiling and optimizing the performance of a React application using the example of a space mission control panel:
Step 1: Identifying the problem
Users report that the control panel is slow, especially when displaying telemetry data and the event list.
Step 2: Profiling the application
We use React DevTools Profiler to identify the source of the problem:
- We record a session during normal panel use
- We analyze the results in the Profiler
- We discover that:
- The
TelemetryChartcomponent renders very slowly (>100ms) - The
EventsListcomponent re-renders every time new telemetry data arrives - The
calculateTrendsfunction takes a lot of CPU time
- The
Step 3: Optimization
Based on the profiling results, we introduce the following optimizations:
- Optimizing TelemetryChart:
1// Before optimization
2function TelemetryChart({ data }) {
3 // Heavy data processing on every render
4 const chartData = processChartData(data);
5
6 return <LineChart data={chartData} />;
7}
8
9// After optimization
10const TelemetryChart = React.memo(function TelemetryChart({ data }) {
11 // Memoized data processing
12 const chartData = useMemo(() => processChartData(data), [data]);
13
14 return <LineChart data={chartData} />;
15});- Optimizing EventsList:
1// Before optimization
2function EventsList({ events }) {
3 return (
4 <div>
5 {events.map(event => (
6 <EventItem key={event.id} event={event} />
7 ))}
8 </div>
9 );
10}
11
12// After optimization
13const EventsList = React.memo(function EventsList({ events }) {
14 return (
15 <div>
16 {events.map(event => (
17 <MemoizedEventItem key={event.id} event={event} />
18 ))}
19 </div>
20 );
21});
22
23const MemoizedEventItem = React.memo(EventItem);- Moving heavy computations to a web worker:
1// Before optimization
2function MissionControlPanel({ telemetryData }) {
3 const trends = calculateTrends(telemetryData); // Blocks the main thread
4
5 return (
6 <div>
7 <TrendsDisplay trends={trends} />
8 {/* other components */}
9 </div>
10 );
11}
12
13// After optimization
14function MissionControlPanel({ telemetryData }) {
15 const [trends, setTrends] = useState(null);
16
17 useEffect(() => {
18 if (!telemetryData) return;
19
20 // Create a web worker
21 const worker = new Worker('./trendsWorker.js');
22
23 // Listen for response
24 worker.addEventListener('message', (event) => {
25 setTrends(event.data);
26 worker.terminate();
27 });
28
29 // Send data for processing
30 worker.postMessage(telemetryData);
31 }, [telemetryData]);
32
33 return (
34 <div>
35 {trends ? <TrendsDisplay trends={trends} /> : <LoadingSpinner />}
36 {/* other components */}
37 </div>
38 );
39}Step 4: Verifying optimizations
After introducing optimizations, we profile the application again:
TelemetryChartrendering time dropped from >100ms to ~15ms- The
EventsListcomponent no longer renders on telemetry data updates - The main thread is no longer blocked by
calculateTrends
Thanks to these optimizations, the user interface runs smoothly, and the control panel responds immediately to user interactions.
Best practices for profiling
Profile in a production-like environment
- Run the application in production mode (
npm run buildand serve the built files) - Test on devices similar to those used by users
- Run the application in production mode (
Establish baselines
- Measure performance before and after optimizations
- Set specific performance goals (e.g., first render time < 2s)
Profile regularly
- Include profiling in the application development cycle
- Monitor performance after introducing major changes
Focus on user perception
- Prioritize optimizations that have the greatest impact on perceived performance
- Remember key Web Vitals metrics (LCP, FID, CLS)
Document profiling results
- Record performance data before and after optimizations
- Share knowledge and optimization techniques with the team
Summary
Performance profiling is an essential element of creating high-quality React applications. Just as space engineers monitor every aspect of a ship's operation, we should continuously analyze the performance of our applications to ensure users have a smooth and responsive experience.
Key points to remember:
- Use the right tools - React DevTools Profiler, Chrome Performance Panel
- Identify bottlenecks - find components that render too often or too long
- Apply appropriate optimization techniques - memoization, avoiding unnecessary renders, list virtualization
- Measure the effectiveness of optimizations - compare performance before and after changes
- Optimize in moderation - remember that premature optimization can lead to overly complicated code
With these profiling techniques and tools, you will be able to create performant React applications that provide an excellent experience for users, even in the most complex cosmic control panel interfaces.
Code for this lesson: App.jsx
1import React, { useState, useMemo, useCallback, useEffect, useRef, Profiler } from 'react';
2
3// ============================================
4// REACT PERFORMANCE PROFILING
5// ============================================
6// This example demonstrates optimization techniques
7// and profiling of React components.
8
9// --- Profiling callback ---
10function onRenderCallback(id, phase, actualDuration, baseDuration) {
11 console.log(
12 `[Profiler] ${id} | phase: ${phase} | actual: ${actualDuration.toFixed(2)}ms | base: ${baseDuration.toFixed(2)}ms`
13 );
14}
15
16// --- Simulating a heavy computation ---
17function expensiveCalculation(items) {
18 let result = 0;
19 for (let i = 0; i < items.length; i++) {
20 for (let j = 0; j < 1000; j++) {
21 result += Math.sqrt(items[i].value * j + 1);
22 }
23 }
24 return result.toFixed(2);
25}
26
27// --- Component WITHOUT optimization ---
28function UnoptimizedChild({ label, onClick }) {
29 const renderCount = useRef(0);
30 renderCount.current += 1;
31
32 return (
33 <div style={childStyle}>
34 <span>{label}</span>
35 <span style={renderBadge}>{renderCount.current}x rendered</span>
36 <button onClick={onClick} style={btnStyle}>Click</button>
37 </div>
38 );
39}
40
41// --- Component WITH optimization (React.memo) ---
42const OptimizedChild = React.memo(function OptimizedChild({ label, onClick }) {
43 const renderCount = useRef(0);
44 renderCount.current += 1;
45
46 return (
47 <div style={{ ...childStyle, borderColor: '#4CAF50' }}>
48 <span>{label}</span>
49 <span style={{ ...renderBadge, background: '#4CAF50' }}>{renderCount.current}x rendered</span>
50 <button onClick={onClick} style={{ ...btnStyle, background: '#4CAF50' }}>Click</button>
51 </div>
52 );
53});
54
55// --- List component with virtualization (simplified) ---
56function VirtualizedList({ items, itemHeight = 40, containerHeight = 200 }) {
57 const [scrollTop, setScrollTop] = useState(0);
58 const containerRef = useRef(null);
59
60 const visibleStart = Math.floor(scrollTop / itemHeight);
61 const visibleCount = Math.ceil(containerHeight / itemHeight) + 1;
62 const visibleItems = items.slice(visibleStart, visibleStart + visibleCount);
63
64 return (
65 <div
66 ref={containerRef}
67 onScroll={(e) => setScrollTop(e.target.scrollTop)}
68 style={{
69 height: containerHeight, overflow: 'auto',
70 background: '#0d1b2a', borderRadius: '8px', border: '1px solid #2a3f5f',
71 }}
72 >
73 <div style={{ height: items.length * itemHeight, position: 'relative' }}>
74 {visibleItems.map((item, i) => (
75 <div
76 key={item.id}
77 style={{
78 position: 'absolute', top: (visibleStart + i) * itemHeight,
79 height: itemHeight, width: '100%', padding: '8px 12px',
80 boxSizing: 'border-box', borderBottom: '1px solid #1b2838',
81 color: '#e0e1dd', display: 'flex', alignItems: 'center',
82 }}
83 >
84 {item.name} - value: {item.value}
85 </div>
86 ))}
87 </div>
88 </div>
89 );
90}
91
92// --- Main application ---
93function App() {
94 const [count, setCount] = useState(0);
95 const [filterText, setFilterText] = useState('');
96 const [showOptimized, setShowOptimized] = useState(false);
97
98 // We generate a list of 500 elements
99 const largeList = useMemo(() => {
100 return Array.from({ length: 500 }, (_, i) => ({
101 id: i,
102 name: `Mission #${i + 1}`,
103 value: Math.floor(Math.random() * 1000),
104 }));
105 }, []);
106
107 // Filtering with useMemo - calculations run only when data changes
108 const filteredItems = useMemo(() => {
109 return largeList.filter((item) =>
110 item.name.toLowerCase().includes(filterText.toLowerCase())
111 );
112 }, [largeList, filterText]);
113
114 // Heavy computation with memoization
115 const expensiveResult = useMemo(() => {
116 return expensiveCalculation(filteredItems.slice(0, 50));
117 }, [filteredItems]);
118
119 // Callback WITHOUT memoization (new reference on every render)
120 const handleClickUnoptimized = () => {
121 console.log('Click without memoization');
122 };
123
124 // Callback WITH memoization (stable reference)
125 const handleClickOptimized = useCallback(() => {
126 console.log('Click with useCallback');
127 }, []);
128
129 return (
130 <div style={appStyle}>
131 <h1 style={{ color: '#ffd700', textAlign: 'center', marginBottom: '8px' }}>
132 React Performance Profiling
133 </h1>
134 <p style={{ textAlign: 'center', color: '#aaa', marginBottom: '20px' }}>
135 Open the browser console to see the Profiler logs
136 </p>
137
138 {/* Section 1: React.memo + useCallback */}
139 <Profiler id="MemoDemo" onRender={onRenderCallback}>
140 <div style={sectionStyle}>
141 <h2 style={headingStyle}>React.memo + useCallback</h2>
142 <p style={{ color: '#aaa', marginBottom: '12px' }}>
143 Click "+1" and observe which component re-renders.
144 </p>
145 <div style={{ textAlign: 'center', marginBottom: '16px' }}>
146 <span style={{ fontSize: '24px', marginRight: '12px' }}>Counter: {count}</span>
147 <button onClick={() => setCount((c) => c + 1)} style={btnAccent}>+1</button>
148 </div>
149
150 <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
151 <div style={{ flex: 1, minWidth: '220px' }}>
152 <h3 style={{ color: '#ff6b6b' }}>Without optimization</h3>
153 <UnoptimizedChild
154 label="Component A"
155 onClick={handleClickUnoptimized}
156 />
157 </div>
158 <div style={{ flex: 1, minWidth: '220px' }}>
159 <h3 style={{ color: '#4CAF50' }}>With React.memo + useCallback</h3>
160 <OptimizedChild
161 label="Component B"
162 onClick={handleClickOptimized}
163 />
164 </div>
165 </div>
166 </div>
167 </Profiler>
168
169 {/* Section 2: useMemo on heavy computations */}
170 <Profiler id="UseMemoDemo" onRender={onRenderCallback}>
171 <div style={sectionStyle}>
172 <h2 style={headingStyle}>useMemo - heavy computations</h2>
173 <p style={{ color: '#aaa', marginBottom: '12px' }}>
174 Filtering and calculations are memoized - changing the filter recalculates the result.
175 </p>
176 <input
177 value={filterText}
178 onChange={(e) => setFilterText(e.target.value)}
179 placeholder="Filter missions..."
180 style={inputStyle}
181 />
182 <p style={{ margin: '8px 0', color: '#ffd700' }}>
183 Calculation result: {expensiveResult} | Found: {filteredItems.length} missions
184 </p>
185 </div>
186 </Profiler>
187
188 {/* Section 3: List virtualization */}
189 <Profiler id="VirtualizedListDemo" onRender={onRenderCallback}>
190 <div style={sectionStyle}>
191 <h2 style={headingStyle}>List virtualization (500 elements)</h2>
192 <p style={{ color: '#aaa', marginBottom: '12px' }}>
193 Only visible elements are rendered - scroll the list.
194 </p>
195 <label style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px', cursor: 'pointer' }}>
196 <input
197 type="checkbox"
198 checked={showOptimized}
199 onChange={() => setShowOptimized(!showOptimized)}
200 />
201 Show virtualized list
202 </label>
203
204 {showOptimized ? (
205 <VirtualizedList items={filteredItems} />
206 ) : (
207 <div style={{
208 height: 200, overflow: 'auto', background: '#0d1b2a',
209 borderRadius: '8px', border: '1px solid #2a3f5f',
210 }}>
211 {filteredItems.map((item) => (
212 <div key={item.id} style={{
213 padding: '8px 12px', borderBottom: '1px solid #1b2838',
214 color: '#e0e1dd',
215 }}>
216 {item.name} - value: {item.value}
217 </div>
218 ))}
219 </div>
220 )}
221 </div>
222 </Profiler>
223
224 {/* TODO: Add a section comparing the performance
225 of two approaches to state updates:
226 1. Multiple separate useState calls (temperature, pressure, oxygen)
227 2. One useState with an object { temperature, pressure, oxygen }
228 Use the Profiler component to measure the differences. */}
229 </div>
230 );
231}
232
233// --- Style ---
234const appStyle = {
235 background: '#0d1b2a', minHeight: '100vh', padding: '20px',
236 fontFamily: 'Arial, sans-serif', color: '#e0e1dd',
237};
238const sectionStyle = {
239 background: '#1b2838', borderRadius: '12px', padding: '20px',
240 maxWidth: '700px', margin: '16px auto', border: '1px solid #2a3f5f',
241};
242const headingStyle = { color: '#00d4ff', marginBottom: '8px' };
243const childStyle = {
244 display: 'flex', alignItems: 'center', gap: '10px',
245 padding: '10px', background: '#0d1b2a', borderRadius: '8px',
246 border: '1px solid #ff6b6b', marginBottom: '8px',
247};
248const renderBadge = {
249 background: '#ff6b6b', color: '#fff', padding: '2px 8px',
250 borderRadius: '12px', fontSize: '12px',
251};
252const btnStyle = {
253 marginLeft: 'auto', padding: '6px 14px', background: '#ff6b6b',
254 color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer',
255};
256const btnAccent = {
257 padding: '8px 20px', background: '#ffd700', color: '#000',
258 border: 'none', borderRadius: '8px', cursor: 'pointer', fontWeight: 'bold',
259};
260const inputStyle = {
261 width: '100%', padding: '8px 12px', background: '#0d1b2a',
262 border: '1px solid #2a3f5f', borderRadius: '6px', color: '#e0e1dd',
263 fontSize: '14px', boxSizing: 'border-box',
264};
265
266export default App;Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What does React.memo() do and when should you use it?
2. What is React Profiler in React DevTools used for?
Hands-on tasks in the game
- Code editor
Dynamic Module Loading
- Horizontal ordering
Arrange the Suspense syntax with fallback UI:
- Vertical ordering
Arrange testing strategies by priority:
- Code editor
Custom hook useForm with validation