<h1 dir="auto">Feature request</h1>
<p dir="auto">When using a <code class="notranslate">next export</code> static build, I would like the <code class="notranslate">getInitialProps</code> method to fire only during the build step and not on the client for select page components. (<a href="https://stackoverflow.com/questions/57897785/how-can-i-use-getinitialprops-only-during-the-nextjs-site-build/57905091" rel="nofollow">Related StackOverflow question</a>)</p>
<p dir="auto">On a static client, Next.js invokes this method before the page component is rendered in order to provide the necessary props for the component. Thus, large requests can delay the client's first paint as this is a blocking request.</p>
<p dir="auto">I would like to selectively ignore <code class="notranslate">getInitialProps</code> for a page component, and instead use the props provided by <code class="notranslate">getInitialProps</code> during <code class="notranslate">next export</code>.</p>
<p dir="auto">I'm unwilling to move my slow API request to <code class="notranslate">componentDidMount</code> in order to avoid the blocking request because I want to use the data returned during the build step to populate the static HTML, and this particular request doesn't need to be dynamic or updated after the build.</p>
<blockquote>
<p dir="auto">Shallow routing works only for same page URL changes.</p>
</blockquote>
<p dir="auto">- <a href="https://nextjs.org/docs/#shallow-routing" rel="nofollow">Shallow Routing</a></p>
<p dir="auto">For this feature request, is it possible to implement something similar to shallow routing, but for <em>new</em> pages?</p>
<p dir="auto">To circumvent <code class="notranslate">getInitialProps</code> on static exports, I currently have to use a standard JSX anchor <code class="notranslate">&lt;a href="/next-page"&gt;</code> to avoid invoking the method. This does cause a full page refresh which is a poor solution.</p>
<h2 dir="auto">Summary</h2>
<p dir="auto">Allow Next.js to become fully hybrid by providing methods to do both static generation and server-side rendering on a per-page basis.</p>
<ul dir="auto">
<li>Two new per-page data fetching methods
<ul dir="auto">
<li><code class="notranslate">getStaticProps</code> - Opt-in to static generation (SSG) at <code class="notranslate">next build</code> time.</li>
<li><code class="notranslate">getServerSideProps</code> - Opt-in to server-side rendering (SSR) which renders on-demand.</li>
</ul>
</li>
<li>A new method for statically generating (SSG) a set of routes from dynamic sources
<ul dir="auto">
<li><code class="notranslate">getStaticPaths</code> - Return list of parameters for dynamic routes to do static generation (SSG)</li>
</ul>
</li>
</ul>
<p dir="auto">This RFC exclusively discusses API additions. All new functionality is completely backwards compatible and can be incrementally adopted. This RFC introduces no deprecations.</p>
<h2 dir="auto">Background</h2>
<p dir="auto">When building websites or web applications you generally have to choose between 2 strategies: Static generation (SSG) or server-side rendering (SSR).</p>
<p dir="auto">Next.js instead lets you build hybrid applications that allow you to choose per-page which strategy is used. Starting with Next.js 9, pages without <code class="notranslate">getInitialProps</code> get <a href="https://nextjs.org/blog/next-9#automatic-static-optimization" rel="nofollow">statically optimized</a> and output as <code class="notranslate">.html</code> files upon <code class="notranslate">next build</code>.</p>
<p dir="auto">However, you might want to do data fetching while generating static pages for your specific use case.</p>
<p dir="auto">For example, to statically generate marketing pages from a CMS or a blog section of the site.</p>
<p dir="auto">Using <code class="notranslate">getInitialProps</code> would opt you into SSR in that case.</p>
<p dir="auto">Next.js currently has a <code class="notranslate">next export</code> command, that makes the application fully SSG, losing the hybrid nature of Next.js.</p>
<p dir="auto">If you use <code class="notranslate">next export</code> with <code class="notranslate">getInitialProps</code> there is another problem that surfaces. <code class="notranslate">getInitialProps</code> is called at build time (which is great), but then when you use <code class="notranslate">next/link</code> to move between pages <code class="notranslate">getInitialProps</code> is called client-side, instead of using the <code class="notranslate">next export</code> result.</p>
<p dir="auto">This also means the data source (CMS / API endpoint) is called directly on client-side transitions, if your data source is down, client-side transitions break while moving between pages.</p>
<p dir="auto">We've collaborated with heavy users of SSG and <code class="notranslate">next export</code> in Next.js like HashiCorp (thanks <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/jescalan/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/jescalan">@jescalan</a>) and extensively investigated the right constraints for introducing two new data fetching methods: <code class="notranslate">getStaticProps</code> and <code class="notranslate">getServerSideProps</code>. But also a way to provide parameters to statically generate static pages for dynamic routes: <code class="notranslate">getStaticPaths</code> (replacement for <code class="notranslate">exportPathMap</code> that is per-page).</p>
<p dir="auto">These new methods have many advantages over the <code class="notranslate">getInitialProps</code> model as there is a clear distinction between what will become SSG vs SSR.</p>
<ul dir="auto">
<li><code class="notranslate">getStaticProps</code> marks the page to be statically generated at build time (when running <code class="notranslate">next build</code>)</li>
<li><code class="notranslate">getStaticPaths</code> allows for returning a list of parameters to generate at build time for dynamic routes</li>
<li><code class="notranslate">getServerSideProps</code> marks the page to be server-side rendered on every request and is the most similar to the current <code class="notranslate">getInitialProps</code> behavior when using a server.</li>
</ul>
<p dir="auto">Separating these methods also allows us to provide the correct context object that can be typed using TypeScript. When you opt for a specific rendering strategy you get the correct values, currently with <code class="notranslate">getInitialProps</code> you have to guess what is available on SSG vs SSR when using TypeScript.</p>
<p dir="auto">Furthermore, by making these methods explicit, it'll allow us to document the different trade-offs more clearly.</p>
<h2 dir="auto">Implementation</h2>
<p dir="auto">Note that all these methods are top-level on the page component file and can't be nested, similar to <code class="notranslate">getInitialProps</code>.</p>
<h3 dir="auto"><code class="notranslate">getStaticProps</code></h3>
<p dir="auto">Using <code class="notranslate">getStaticProps</code> means the page will be rendered statically at build time (SSG).</p>
<p dir="auto">This new method will allow you to do data fetching for a page that will be statically generated into a <code class="notranslate">.html</code> file at <code class="notranslate">next build</code> time.</p>
<p dir="auto">Next.js will also automatically generate a JSON file that holds the result of <code class="notranslate">getStaticProps</code> at <code class="notranslate">next build</code> time. This is being used for client-side routing.</p>
<p dir="auto">When client-side routing through <code class="notranslate">next/link</code> or <code class="notranslate">next/router</code>, Next.js will fetch this JSON file to get the props needed to render the page client-side.</p>
<p dir="auto">Properties are returned under a <code class="notranslate">props</code> key so that other options can be introduced in the future.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// pages/index.js

// getStaticProps is only called server-side
// In theory you could do direct database queries
export async function getStaticProps(context) {
  return {
    // Unlike `getInitialProps` the props are returned under a props key
    // The reasoning behind this is that there's potentially more options
    // that will be introduced in the future.
    // For example to allow you to further control behavior per-page.
    props: {}
  };
}"><pre class="notranslate"><span class="pl-c">// pages/index.js</span>

<span class="pl-c">// getStaticProps is only called server-side</span>
<span class="pl-c">// In theory you could do direct database queries</span>
<span class="pl-k">export</span> <span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">getStaticProps</span><span class="pl-kos">(</span><span class="pl-s1">context</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-kos">{</span>
    <span class="pl-c">// Unlike `getInitialProps` the props are returned under a props key</span>
    <span class="pl-c">// The reasoning behind this is that there's potentially more options</span>
    <span class="pl-c">// that will be introduced in the future.</span>
    <span class="pl-c">// For example to allow you to further control behavior per-page.</span>
    <span class="pl-c1">props</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">The <code class="notranslate">context</code> will contain:</p>
<ul dir="auto">
<li><code class="notranslate">params</code> - The parameters when on a dynamic route.</li>
</ul>
<h3 dir="auto"><code class="notranslate">getStaticPaths</code></h3>
<p dir="auto">This is an extension on <code class="notranslate">getStaticProps</code> usage for dynamic routes.</p>
<p dir="auto"><code class="notranslate">getStaticPaths</code> replaces the need for having a <code class="notranslate">exportPathMap</code> and works per-page.</p>
<p dir="auto">Since you might want to statically generate a list of urls that have a dynamic parameter, like in the example below a <code class="notranslate">slug</code>. Next.js will provide a <code class="notranslate">getStaticPaths</code> method that allows for returning a list of urls. Since it's a <code class="notranslate">async</code> method you can also fetch that list from a data source like your CMS.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// pages/blog/[slug].js

// `getStaticProps` gets a `params` object holding the dynamic parameters
// For `/blog/hello-world` it would look like `{ slug: 'hello-world }`
export async function getStaticProps({ params }) {
  return {
    props: {}
  };
}

// `getStaticPaths` allows the user to return a list of parameters to
// render to HTML at build time.
export async function getStaticPaths() {
  return {
    paths: [
      // This renders /blog/hello-world to HTML at build time
      { params: { slug: &quot;hello-world&quot; } }
    ]
  };
}"><pre class="notranslate"><span class="pl-c">// pages/blog/[slug].js</span>

<span class="pl-c">// `getStaticProps` gets a `params` object holding the dynamic parameters</span>
<span class="pl-c">// For `/blog/hello-world` it would look like `{ slug: 'hello-world }`</span>
<span class="pl-k">export</span> <span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">getStaticProps</span><span class="pl-kos">(</span><span class="pl-kos">{</span> params <span class="pl-kos">}</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-kos">{</span>
    <span class="pl-c1">props</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-c">// `getStaticPaths` allows the user to return a list of parameters to</span>
<span class="pl-c">// render to HTML at build time.</span>
<span class="pl-k">export</span> <span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">getStaticPaths</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-kos">{</span>
    <span class="pl-c1">paths</span>: <span class="pl-kos">[</span>
      <span class="pl-c">// This renders /blog/hello-world to HTML at build time</span>
      <span class="pl-kos">{</span> <span class="pl-c1">params</span>: <span class="pl-kos">{</span> <span class="pl-c1">slug</span>: <span class="pl-s">"hello-world"</span> <span class="pl-kos">}</span> <span class="pl-kos">}</span>
    <span class="pl-kos">]</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<h4 dir="auto">Fallback</h4>
<p dir="auto">In many cases you might not want to pre-render every possible route in your application at build-time (for example if you have millions of products). For this reason Next.js will automatically generate a <code class="notranslate">fallback</code> page that is a render of the page without data (so that a loading state can be shown) for when the page hasn’t been generated yet.</p>
<p dir="auto">The exact behavior of serving will be:</p>
<ul dir="auto">
<li>Incoming request
<ul dir="auto">
<li>Next.js checks if the path was generated at build time
<ul dir="auto">
<li>If the path was generated
<ul dir="auto">
<li>serve directly</li>
</ul>
</li>
<li>If the path was not generated
<ul dir="auto">
<li>Serve the fallback
<ul dir="auto">
<li>Next.js renders the page (with data) in the background and adds it to the list of generated pages</li>
<li>Subsequent request to the same path will serve the generated page</li>
<li>This ensures that users always have a fast experience and never have slow TTFB from server-rendering while preserving fast builds and static-generation properties</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p dir="auto">In case you want paths that weren’t generated at build time to result in a 404 that is also possible by returning <code class="notranslate">fallback: false</code> from <code class="notranslate">getStaticPaths</code></p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// `getStaticPaths` allows the user to return a list of parameters to
// render to HTML at build time.
export async function getStaticPaths() {
  return {
    // Opt-out of the described fallback behavior
    fallback: false,
    paths: [
      // This renders /blog/hello-world to HTML at build time
      { params: { slug: &quot;hello-world&quot; } }
    ]
  };
}"><pre class="notranslate"><span class="pl-c">// `getStaticPaths` allows the user to return a list of parameters to</span>
<span class="pl-c">// render to HTML at build time.</span>
<span class="pl-k">export</span> <span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">getStaticPaths</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-kos">{</span>
    <span class="pl-c">// Opt-out of the described fallback behavior</span>
    <span class="pl-c1">fallback</span>: <span class="pl-c1">false</span><span class="pl-kos">,</span>
    <span class="pl-c1">paths</span>: <span class="pl-kos">[</span>
      <span class="pl-c">// This renders /blog/hello-world to HTML at build time</span>
      <span class="pl-kos">{</span> <span class="pl-c1">params</span>: <span class="pl-kos">{</span> <span class="pl-c1">slug</span>: <span class="pl-s">"hello-world"</span> <span class="pl-kos">}</span> <span class="pl-kos">}</span>
    <span class="pl-kos">]</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<h3 dir="auto"><code class="notranslate">getServerSideProps</code></h3>
<p dir="auto">When using <code class="notranslate">getServerSideProps</code>, the page is not statically generated (SSG) and instead renders on-demand on every request to the server (SSR).</p>
<p dir="auto">Next.js will also automatically expose an API endpoint that returns the result of calling <code class="notranslate">getServerSideProps</code>. This is being used for client-side routing.</p>
<p dir="auto">When client-side routing through <code class="notranslate">next/link</code> or <code class="notranslate">next/router</code>, Next.js will fetch this exposed API endpoint to get the JSON data that is turned into the props needed to render the page client-side.</p>
<p dir="auto">This method is the most similar to the current <code class="notranslate">getInitialProps</code>, with the main difference being <code class="notranslate">getServerSideProps</code> is always executed server-side instead of in the browser. Either on server-side rendering or the API fetch when client-side routing.</p>
<p dir="auto">Similarly to <code class="notranslate">getStaticProps</code> the properties are returned under a <code class="notranslate">props</code> key.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// pages/index.js

// getServerSideProps is only called server-side
// In theory you could do direct database queries
export async function getServerSideProps(context) {
  return {
    // Unlike `getInitialProps` the props are returned under a props key
    // The reasoning behind this is that there's potentially more options
    // that will be introduced in the future.
    // For example to allow you to further control behavior per-page.
    props: {}
  };
}"><pre class="notranslate"><span class="pl-c">// pages/index.js</span>

<span class="pl-c">// getServerSideProps is only called server-side</span>
<span class="pl-c">// In theory you could do direct database queries</span>
<span class="pl-k">export</span> <span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">getServerSideProps</span><span class="pl-kos">(</span><span class="pl-s1">context</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-kos">{</span>
    <span class="pl-c">// Unlike `getInitialProps` the props are returned under a props key</span>
    <span class="pl-c">// The reasoning behind this is that there's potentially more options</span>
    <span class="pl-c">// that will be introduced in the future.</span>
    <span class="pl-c">// For example to allow you to further control behavior per-page.</span>
    <span class="pl-c1">props</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">The <code class="notranslate">context</code> will contain:</p>
<ul dir="auto">
<li><code class="notranslate">params</code> - The parameters on a dynamic route</li>
<li><code class="notranslate">req</code> - The HTTP request object</li>
<li><code class="notranslate">res</code> - The HTTP response object</li>
<li><code class="notranslate">query</code> - The query string (not entirely sure about this one, but probably needed)</li>
</ul>
<hr>
<p dir="auto">Authored by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/timneutkens/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/timneutkens">@timneutkens</a>, <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/Timer/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/Timer">@Timer</a>, <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/ijjk/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/ijjk">@ijjk</a>, <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/lfades/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/lfades">@lfades</a>. Collaborated with <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/rauchg/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/rauchg">@rauchg</a>, <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/jescalan/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/jescalan">@jescalan</a> and others <g-emoji class="g-emoji" alias="rocket" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png">🚀</g-emoji></p>