Arthur Nikitsin, a seasoned developer at FocusReactive, has published an insightful article on static site generation. But even static websites can leverage various content-loading methods for enhanced performance and flexibility. In this overview, we'll explore six effective methods available in Next.js v14 using the pages router. These methods ensure optimal performance and user experience, even with SSG. Note that the app router offers more flexibility, but is recommended to wait for the stable release in v15.
Check out Arthur's original article for a detailed dive into static site generation.
1. Static Site Generation (SSG)
Generates HTML at build time and reuses it for each request. Ideal for pages with content that doesn’t change often. This is the primary method used in static site generation.
// pages/index.js
export async function getStaticProps() {
const data = await fetchData();
return { props: { data } };
}
When to use: For static content like blogs or documentation.
API: getStaticProps
How it works: Fetches data at build time, creating HTML that is served on every request.
Read more about SSG
2. Server-Side Rendering (SSR)
Generates HTML on each request, useful for dynamic content.
// pages/index.js
export async function getServerSideProps() {
const data = await fetchData();
return { props: { data } };
}
When to use: For frequently changing data.
API: getServerSideProps
How it works: Fetches data on every request, rendering HTML dynamically.
Read more about SSR
3. Client-Side Rendering (CSR)
Data is fetched and rendered on the client side, which can be beneficial even for static sites. This method is useful when you need to update parts of the page based on user interactions without a full page reload.
// pages/index.js
useEffect(() => {
async function fetchData() {
const response = await fetch('/api/data');
setData(await response.json());
}
fetchData();
}, []);
When to use: For interactive elements like forms, real-time data, or personalized content that updates after the initial page load.
API: useEffect
How it works: Fetches data in the browser after the initial load, allowing for dynamic updates without a full reload. This enhances user experience by providing instant feedback or updates based on user interactions.
Read more about CSR
4. Code Splitting
Dynamically imports parts of code to optimize load time. Code splitting can also be used for lazy loading components (see the next point).
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() => import('../components/DynamicComponent'));
When to use: To improve performance by loading only necessary code.
API: next/dynamic
How it works: Loads code chunks on demand, reducing initial load time.
Read more about Code Splitting
5. Lazy Loading Components
Defers loading of offscreen components until needed, enhancing performance.
import dynamic from 'next/dynamic'
const DynamicHeader = dynamic(() => import('../components/header'), {
loading: () => <p>Loading...</p>,
})
export default function Home() {
return <DynamicHeader />
}
When to use: For components not immediately in view.
API: React.lazy
How it works: Loads components when they are about to enter the viewport.
Read more about Lazy Loading Components
6. Lazy Loading Images
By default, the next/image
component uses lazy loading for images. The priority
prop can be used to disable lazy loading when necessary.
import Image from 'next/image';
const MyComponent = () => (
<Image
src="/path/to/image.jpg"
alt="Description"
width={500}
height={500}
priority // use it to disable lazy loading
/>
);
When to use: To optimize page speed with many images.
API: next/image
How it works: Defers image loading until they are needed, reducing initial load time.
Read more about lazy loading images
Conclusion
These methods in Next.js v14 can significantly improve your static site's performance and user experience. For more detailed insights, read Arthur Nikitsin's full article. You can explore more of his work in our company blog.