Embedded Data in Next.js: Why It Hurts Performance and How to Avoid It
The size of your Next.js application's client-side bundle directly impacts your website’s loading speed, user experience, and ad revenue. A common mistake is embedding large JavaScript/JSON objects or Base64-encoded images in the client-side code. This practice can balloon bundle sizes, slow page loads, and ultimately hurt your product’s performance.
In this article, we’ll highlight why embedding large data in the client code is problematic, showcase examples of bad practices, and provide actionable solutions to keep your Next.js projects optimized.
Bad Practice 1: Embedding Large JSON Objects
A common mistake in Next.js projects is storing significant datasets directly in client-side code. Consider this example:
'use client';
const usersDatabase = [
{ id: 1, name: 'John Doe', email: '[email protected]' },
{ id: 2, name: 'Jane Doe', email: '[email protected]' },
// Imagine this list has tens of thousands of entries...
];
export default function UserList() {
return (
<div>
{usersDatabase.map((user) => (
<p key={user.id}>{user.name}</p>
))}
</div>
);
}
Why it’s bad:
- The entire dataset is bundled with the JavaScript code and sent to the client.
- This bloats the bundle size, increases download times, and impacts Time to Interactive (TTI).
- Parsing large datasets in the browser consumes CPU resources, slowing down rendering.
Bad Practice 2: Embedding Base64 Images
Another pitfall is embedding large images directly in the client-side JavaScript.
'use client';
const imageData = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA..."; // Truncated for brevity
export default function DisplayImage() {
return <img src={imageData} alt="Example" />;
}
Why it’s bad:
- Base64-encoded images are bulkier than their compressed binary counterparts.
- These images unnecessarily increase the JavaScript bundle size.
- Every time the page loads, the browser must decode these images, adding CPU overhead.
Bad Practice 3: Hardcoding Configurations in Client Code
Sometimes, developers embed large configuration objects or API keys directly in the client code.
'use client';
const appConfig = {
apiUrl: "https://api.example.com",
settings: {
theme: "dark",
features: {
experimental: true,
// Contains deeply nested settings
},
},
};
export default function ConfigInfo() {
return <pre>{JSON.stringify(appConfig, null, 2)}</pre>;
}
Why it’s bad:
- Large configuration objects inflate the bundle size unnecessarily.
- Sensitive data, such as API keys, can become exposed to users.
- These configurations should live on the server or in environment variables.
Good Practices: How to Optimize Your Next.js Code
Use Server-Side APIs for Large Datasets
Instead of embedding datasets in your client-side code, fetch them dynamically using server-side APIs.
'use client';
import { useState, useEffect } from 'react';
export default function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users') // Fetch data from a server API
.then((response) => response.json())
.then((data) => setUsers(data));
}, []);
return (
<div>
{users.map((user) => (
<p key={user.id}>{user.name}</p>
))}
</div>
);
}
Benefits:
- The dataset remains on the server, reducing bundle size.
- You can use caching strategies to further optimize data fetching.
Serve Images via CDN
Instead of embedding Base64 images, serve them through a Content Delivery Network (CDN).
'use client';
export default function DisplayImage() {
const imageUrl = "https://cdn.example.com/images/example.png";
return <img src={imageUrl} alt="Example" />;
}
Benefits:
- CDNs are optimized for delivering media content quickly and efficiently.
- You can use lazy loading (loading="lazy") to further improve performance.
Optimize Configuration Management
Store large configuration objects and sensitive data securely on the server. Use environment variables and API endpoints to retrieve necessary configurations.
// Server-side code (e.g., /api/config)
export default function handler(req, res) {
res.status(200).json({
apiUrl: process.env.API_URL,
settings: { theme: "dark", features: { experimental: true } },
});
}
'use client';
import { useEffect, useState } from 'react';
export default function ConfigInfo() {
const [config, setConfig] = useState(null);
useEffect(() => {
fetch('/api/config')
.then((response) => response.json())
.then((data) => setConfig(data));
}, []);
return <pre>{JSON.stringify(config, null, 2)}</pre>;
}
Benefits:
- Keeps sensitive data secure and out of the client bundle.
- Allows for dynamic configuration changes without redeploying the application.
Conclusion
Embedding large data or media directly in the client-side code of your Next.js project is a recipe for poor performance and slow user experiences. By leveraging server-side APIs, CDNs, and proper configuration management, you can significantly reduce your bundle size, improve load times, and ensure a smoother experience for your users.
Remember, performance optimization isn’t just about speed—it’s about delivering value faster, keeping users engaged, and maximizing your website’s potential.
Catch Metrics is the leading solution to help solve ad stack performance problems.Get in touchto learn more from our experts