Problem
Indeed, Next.js is characterized by its Server-Side Rendering (SSR) feature. When using SSR, deploying on Vercel often becomes the standard approach. This is because building it on your own is not easy. If you’re interested, check out open-next.
Deploying on Vercel is convenient, but there are also some issues. Vercel’s SSR renders in a serverless manner, so its scalability is certainly excellent. However, Serverless has a Cold Start issue, which can cause the response to be considerably slow during the initial access. I have explored solutions to this problem.
TL;DR
From version 13.4 of Next.js, the app folder becomes officially available, but it seems that the Cold Start issue for serverless functions has not been resolved. In cases where caching is not possible and SSR (Server-Side Rendering) is required every time, let’s render at the Edge instead of using Node.js for rendering.
It is desirable to avoid using the old getInitialProps, as Edge rendering is not available with it.
Measurement results
Preconditions
- Execute SSR for each request without caching
- Minimize server-side processing time
- Do not consider the response bundle size
- Set Vercel’s server to
Tokyo-hnd1
getInitialProps
Implementation is very easy. Add the following to _app.tsx:
// use getInitialProps to fetch data
MyApp.getInitialProps = async (appContext: AppContext) => {
const appProps: any = await App.getInitialProps(appContext);
const { req } = appContext.ctx;
if (req) {
const host = req.headers.host;
const dataRes = await fetch("https://jsonplaceholder.typicode.com/todos/1");
return { pageProps: appProps.pageProps, host, data: await dataRes.json() };
} else {
const { host, data } = window.__NEXT_DATA__.props;
return { pageProps: appProps.pageProps, host, data };
}
};
It is found that the response time from the server takes 2 seconds or more
for the initial access (Cold Start).
When accessing with an already launched instance, the response time from the server is around 400ms
.
getServerSideProps(Serverless Function)
getServerSideProps can be implemented similarly to getInitialProps.
export const getServerSideProps = async () => {
const dataRes = await fetch("https://jsonplaceholder.typicode.com/todos/1");
return {
props: {
data: await dataRes.json(),
},
};
};
Similar to getInitialProps, it is found that the response time from the server takes 2 seconds or more
for the initial access (Cold Start).
When accessing with an already launched instance, the response time from the server is around 400ms
.
getServerSideProps(Edge Function)
Edge rendering is a promising candidate. With Edge Function, even without caching, the startup time is only about 80ms, and implementation is also very easy. Simply specify the rendering environment in index.tsx
of getServerSideProps
.
export const config = {
runtime: "experimental-edge",
};
It will be significantly improved. Even for the initial access, it will be around 150ms
.
If you access again immediately, it will be around 50ms
.
App Router(Serverless Function)
You can add a function to retrieve data to page.tsx
and use React Server Components.
async function getData() {
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1", {
cache: "no-store",
});
return await res.json();
}
export default async function Home() {
const dataPromise = getData();
const data = await dataPromise;
return (
<Suspense fallback={<div>Loading...</div>}>
<pre>{JSON.stringify(data)}</pre>
</Suspense>
);
}
The initial access (Cold Start) still takes about 2 seconds
. While the issue of bundling is resolved with React Server Components, the problem of Cold Start is not resolved.
When accessing with an already launched instance, the response time from the server is around 400ms
.
appフォルダー(Edge Function)
The implementation is almost the same as for the Serverless Function, just specify the runtime.
export const runtime = "edge";
The response time for the initial access is only around 200ms
.
If you access again immediately, it will be around 40ms
.
Summary
Initial Access (Cold Start) | Access again immediately | |
---|---|---|
getInitialProps | 2s | 400ms |
getServerSideProps(Serverless Function) | 2s | 400ms |
getServerSideProps(Edge Function) | 150ms | 50ms |
appフォルダー(Serverless Function) | 2s | 400ms |
appフォルダー(Edge Function) | 200ms | 40ms |
If it takes about 3 seconds for a website to load, the bounce rate may increase significantly. To reduce the bounce rate of pages where caching cannot be used, it is a good choice to use Edge rendering, which is a trend.