Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static Generation / SSG Improvements #9524

Open
timneutkens opened this issue Nov 25, 2019 · 39 comments
Open

Static Generation / SSG Improvements #9524

timneutkens opened this issue Nov 25, 2019 · 39 comments
Labels
RFC

Comments

@timneutkens
Copy link
Member

@timneutkens timneutkens commented Nov 25, 2019

Summary

Allow Next.js to become fully hybrid by providing methods to do both static generation and server-side rendering on a per-page basis.

  • Two new data per-page fetching methods
    • getStaticProps - Opt-in to static generation (SSG) at next build time.
    • getServerProps - Opt-in to server-side rendering (SSR) which renders on-demand.
  • A new method for statically generating (SSG) a set of routes from dynamic sources
    • getStaticPaths - Return list of parameters for dynamic routes to do static generation (SSG)

This RFC exclusively discusses API additions. All new functionality is completely backwards compatible and can be incrementally adopted. This RFC introduces no deprecations.

Background

When building websites or web applications you generally have to choose between 2 strategies: Static generation (SSG) or server-side rendering (SSR).

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 getInitialProps get statically optimized and output as .html files upon next build.

However, you might want to do data fetching while generating static pages for your specific use case.

For example, to statically generate marketing pages from a CMS or a blog section of the site.

Using getInitialProps would opt you into SSR in that case.

Next.js currently has a next export command, that makes the application fully SSG, losing the hybrid nature of Next.js.

If you use next export with getInitialProps there is another problem that surfaces. getInitialProps is called at build time (which is great), but then when you use next/link to move between pages getInitialProps is called client-side, instead of using the next export result.

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.

We've collaborated with heavy users of SSG and next export in Next.js like HashiCorp (thanks @jescalan) and extensively investigated the right constraints for introducing two new data fetching methods: getStaticProps and getServerProps. But also a way to provide parameters to statically generate static pages for dynamic routes: getStaticPaths (replacement for exportPathMap that is per-page).

These new methods have many advantages over the getInitialProps model as there is a clear distinction between what will become SSG vs SSR.

  • getStaticProps marks the page to be statically generated at build time (when running next build)
  • getStaticPaths allows for returning a list of parameters to generate at build time for dynamic routes
  • getServerProps marks the page to be server-side rendered on every request and is the most similar to the current getInitialProps behavior when using a server.

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 getInitialProps you have to guess what is available on SSG vs SSR when using TypeScript.

Furthermore, by making these methods explicit, it'll allow us to document the different trade-offs more clearly.

Implementation

Note that all these methods are top-level on the page component file and can't be nested, similar to getInitialProps.

getStaticProps

Using getStaticProps means the page will be rendered statically at build time (SSG).

This new method will allow you to do data fetching for a page that will be statically generated into a .html file at next build time.

Next.js will also automatically generate a JSON file that holds the result of getStaticProps at next build time. This is being used for client-side routing.

When client-side routing through next/link or next/router, Next.js will fetch this JSON file to get the props needed to render the page client-side.

Properties are returned under a props key so that other options can be introduced in the future.

// 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: {}
  };
}

The context will contain:

  • params - The parameters when on a dynamic route.

getStaticPaths

This is an extension on getStaticProps usage for dynamic routes.

getStaticPaths replaces the need for having a exportPathMap and works per-page.

Since you might want to statically generate a list of urls that have a dynamic parameter, like in the example below a slug. Next.js will provide a getStaticPaths method that allows for returning a list of urls. Since it's a async method you can also fetch that list from a data source like your CMS.

// 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 [
    // This renders /blog/hello-world to HTML at build time
    { params: { slug: "hello-world" } }
  ];
}

getServerProps

When using getServerProps, the page is not statically generated (SSG) and instead renders on-demand on every request to the server (SSR).

Next.js will also automatically expose an API endpoint that returns the result of calling getServerProps. This is being used for client-side routing.

When client-side routing through next/link or next/router, 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.

This method is the most similar to the current getInitialProps, with the main difference being getServerProps is always executed server-side instead of in the browser. Either on server-side rendering or the API fetch when client-side routing.

Similarly to getStaticProps the properties are returned under a props key.

// pages/index.js

// getServerProps is only called server-side
// In theory you could do direct database queries
export async function getServerProps(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: {}
  };
}

The context will contain:

  • params - The parameters on a dynamic route
  • req - The HTTP request object
  • res - The HTTP response object
  • query - The query string (not entirely sure about this one, but probably needed)

Authored by @timneutkens, @Timer, @ijjk, @lfades. Collaborated with @rauchg, @jescalan and others 🚀

@timneutkens timneutkens added the RFC label Nov 25, 2019
@timneutkens timneutkens changed the title Static Generation (SSG) Improvements Static Generation / SSG Improvements Nov 25, 2019
@maxchehab

This comment has been minimized.

Copy link

@maxchehab maxchehab commented Nov 26, 2019

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: {}
  };
}

I'm interested in seeing what circumstance we would need to return additional data other than what can be contained within props. I found the in-line explanation "to further control behavior per-page" a bit vague.

@neeharv

This comment has been minimized.

Copy link

@neeharv neeharv commented Nov 26, 2019

Looks very interesting! Would his be a replacement for getInitialProps or along side? For eg, for our use case, the data fetching API is a public service. So on client side navigation, we expect the client to directly call the API layer, whereas on SSR the server calls it. Going forward, would this use case continue to be solved by the prior method?

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

I'm interested in seeing what circumstance we would need to return additional data other than what can be contained within props. I found the in-line explanation "to further control behavior per-page" a bit vague.

It's more so about future proofing the method so that it allows us to extend it later on if needed.

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

Looks very interesting! Would his be a replacement for getInitialProps or along side? For eg, for our use case, the data fetching API is a public service. So on client side navigation, we expect the client to directly call the API layer, whereas on SSR the server calls it. Going forward, would this use case continue to be solved by the prior method?

In general that behavior has some downsides, for example waterfall fetches that could be slow from certain areas around the world. The getServerProps approach allows for caching the response more efficiently.

@joltmode

This comment has been minimized.

Copy link

@joltmode joltmode commented Nov 26, 2019

This looks really interesting! Cool idea!

I have concerns about deployment though...

Let's imagine I'm hosting on Now.
For the first deployment, it's obvious that the whole applications gets built on deployment.

Then, I change some content in CMS and am looking to trigger rebuild of SSG pages only, but the application code has not changed.

Immediately alarm goes off, that in this case if I trigger the build, there are two possible solutions:

  1. Nothing gets rebuilt, as everything is cached - no code has changed and blabla.
  2. I --force it, and now "everything" gets rebuilt, but I only required the SSG pages to be rebuilt.

These are just hypotheses, as that depends on build systems themselves - how aware are they of Next

This would probably affect any other hosting solution.

Next itself has a .next/cache... how would this play around that?

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

@joltmode that's basically the case for every static site generator currently. .next/cache is preserved between deployments on Now and reused. Keep in mind that you're currently probably using getInitialProps for this case with caching (potentially https://zeit.co/blog/serverless-pre-rendering), which dynamically renders in a serverless function and then caches on our CDN, this behavior is still totally fine and will continue to work if you use getServerProps.

@kibs

This comment has been minimized.

Copy link

@kibs kibs commented Nov 26, 2019

Really awesome, would fit nicely into how we are using Next for customer projects, and would remove some boilerplate code we copy around.

One thing to consider is the naming of getStaticProps and getServerProps, if they return a { props } and potential other options in the future, would the *Props not be confusing? Maybe getStaticConfiguration, getStaticSetup, getStaticOptions would be more generic?

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

@kibs the return values would always relate to how the props are handled. So the naming is fine imo.

@flawyte

This comment has been minimized.

Copy link

@flawyte flawyte commented Nov 26, 2019

This is simply awesome! This is solving every use case and need I've had recently or could think of while developing both private and professional web apps. You just prevented me from starting my own hybrid site generator, thanks!

I can also relate to the new methods being better than the previous getInitialProps() and exportPathMap(), which sounded a bit confusing to me at first when I started using Next.js and digged into SSR / SSG. The per-page approach makes more sense to me too.

Can't wait to try this out!

Just a side note : in the last example I think getServerProps() is missing a context param.

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

Just a side note : in the last example I think getServerProps() is missing a context param.

Fixed!

@herrstucki

This comment has been minimized.

Copy link
Contributor

@herrstucki herrstucki commented Nov 26, 2019

This sounds great! I just wonder from a TypeScript user perspective if having getStaticProps, getStaticPaths and getServerProps as static methods on the page component (like getInitialProps at the moment) would be easier to type/use correctly.

const Page: NextPage<Props> = (props) => ...

// Explicit types needed here
export const getStaticPaths: NextGetStaticPaths<Params> = () => ...
export const getStaticProps: NextGetStaticProps<Props, Params> = (context) => ...
export const getServerProps: NextGetServerProps<Props> = (context) => ...

export default Page

// vs.

const Page: NextPage<Props, Params> = (props) => ...

// Static method types come from NextPage<Props, Params>
Page.getStaticPaths = () => ...
Page.getStaticProps = (context) => ...
Page.getServerProps = (context) => ..

export default Page
@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

@herrstucki the problem with that approach is that it becomes significantly harder to tree shake (read: close to impossible). Which would mean unnecessary code would be shipped to the browser.

@herrstucki

This comment has been minimized.

Copy link
Contributor

@herrstucki herrstucki commented Nov 26, 2019

@timneutkens good point … but would then a separate file not make even more sense? Or is something like this reliably tree-shakable?

// This should all be removed in client-side code …
import {fetchQuery, queryTag} from 'big-data-fetching-lib';
const query = queryTag`...`
export const getStaticProps = async () => ({ props: await fetchQuery(query) })

// Only this should be included client-side
export default (props) => ...
@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

@herrstucki we can reliably tree shake that, but we're also still discussing having a separate file. Personally I prefer the single file approach though.

@neeharv

This comment has been minimized.

Copy link

@neeharv neeharv commented Nov 26, 2019

Looks very interesting! Would his be a replacement for getInitialProps or along side? For eg, for our use case, the data fetching API is a public service. So on client side navigation, we expect the client to directly call the API layer, whereas on SSR the server calls it. Going forward, would this use case continue to be solved by the prior method?

In general that behavior has some downsides, for example waterfall fetches that could be slow from certain areas around the world. The getServerProps approach allows for caching the response more efficiently.

Sure, but I'm talking about avoiding the RTT to the react server at all. Consider the case where the SSR output from the server is cached at CDN / cache server proxy. Couple that with data fetching for client navigation directly calling a different API layer (common to web / apps / all clients), means that the Next.js server layer doesn't have to be scaled up as much in a high traffic scenario.

I understand your point of waterfall fetches, but giving consumers the ability to treat the Next server as a SSR layer vs a data source would allow for much better scaling setups.

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

Looks very interesting! Would his be a replacement for getInitialProps or along side? For eg, for our use case, the data fetching API is a public service. So on client side navigation, we expect the client to directly call the API layer, whereas on SSR the server calls it. Going forward, would this use case continue to be solved by the prior method?

In general that behavior has some downsides, for example waterfall fetches that could be slow from certain areas around the world. The getServerProps approach allows for caching the response more efficiently.

Sure, but I'm talking about avoiding the RTT to the react server at all. Consider the case where the SSR output from the server is cached at CDN / cache server proxy. Couple that with data fetching for client navigation directly calling a different API layer (common to web / apps / all clients), means that the Next.js server layer doesn't have to be scaled up as much in a high traffic scenario.

I understand your point of waterfall fetches, but giving consumers the ability to treat the Next server as a SSR layer vs a data source would allow for much better scaling setups.

I think you're misunderstanding that this new behavior means you can actually cache the full results on a CDN given the CDN supports dynamic responses. This was previously not reliably possible with getInitialProps.

@jaredpalmer

This comment has been minimized.

Copy link
Contributor

@jaredpalmer jaredpalmer commented Nov 26, 2019

@timneutkens I played around with canary, trying to port some babel-plugin-preval code to getStaticProps. I am ran into an issue with fs.

I am trying to read the .md files of my ./pages/blog/ directory and loop through them so I can make a blog index page with all my posts

import React from 'react';
import Link from 'next/link';
import fs from 'fs-extra';

const Index = ({ posts }) => (
  <div>
    Hello World. <Thing msg="hello" />
    <Link href="/thing">
      <a>About</a>
    </Link>
    {posts.map(p => (
      <div key={p.title}>{p.title}</div>
    ))}
  </div>
);

Index.getStaticProps = async () => {
  const items = await fs.readdir('./pages/blog');
  items.forEach(path => /* .... do some stuff ... */ )
  return { props: { posts: items } };
};

export default Index;

This code leads to this error:

Module not found: Can't resolve 'fs' in '/Users/jared/Downloads/nextjs-typescript-template/node_modules/fs-extra/lib'

IIRC from Razzle, this error has to do with webpack's filesystem stubs (or lack thereof). I think I once fixed this with Razzle by adding this to the webpack config.

node: {
  fs: "empty";
}

I tried this next.config.js, but it just makes the error go away. It appears though that fs/fs-extra doesn't actually work, or it does and perhaps paths don't (unclear to me). Any thoughts on that?

My other question, more generally, is what you imagine best practices will be for using import vs. require in getStaticProps. If I'm not mistaken, my above snippet will attempt to import fs-extra in React isomorphically??. Would it thus be better to change the import to an inline require like this?

Index.getStaticProps = async () => {
  const fs = require('fs-extra');  // only require when needed at SSG
  const props = await fs.readdir('./pages/blog');
  return { props: { posts } };
};
@neeharv

This comment has been minimized.

Copy link

@neeharv neeharv commented Nov 26, 2019

I think you're misunderstanding that this new behavior means you can actually cache the full results on a CDN given the CDN supports dynamic responses. This was previously not reliably possible with getInitialProps.

Ah, I think I get what you mean. Would that mean that getServerProps on the first SSR generation would create a unique endpoint, in a content addressable hash maybe in the URL maybe that we can then cache on the CDN? The only downside of this would be that said cache would not be shareable between non Next apps (android / ios) and Next apps. Additionally, with an external data source, the cache control directives are upstream, but here since Next would assume responsibility of serving up the data, we need APIs or props to specify those for the generated data endpoints.

@herrstucki

This comment has been minimized.

Copy link
Contributor

@herrstucki herrstucki commented Nov 26, 2019

@jaredpalmer I assume #9524 (comment) (including my concern about reliable tree-shakeability) would be resolved by having a separate file that would be compiled completely separately from client bundle code? E.g.

pages/
    foo.js
    foo.data.js (<- exports getStaticProps etc.)

or:

pages/
    foo.js
pages-data/
    foo.js (<- exports getStaticProps etc.)
@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

@jaredpalmer tree shaking wasn't implemented yet on canary.

@baer

This comment has been minimized.

Copy link

@baer baer commented Nov 26, 2019

As always, thank you for everything y'all do. Next.js has been an absolute joy to work with, and as I've said before, just about every feature release lets us reduce the size of the codebases I manage. It's amazing.

It's hard to be critical of this RFC since, as written, it is immediately useful for a LOT of applications. I do, however, want to address one line that I'm not sure I agree with:

"getStaticPaths replaces the need for having a exportPathMap and works per-page."

In some applications, it is either impractical or impossible to know the routes at build time. A few examples would be:

  • User profile pages
  • Product pages (for companies with a fast-changing inventory)
  • Sales Order details pages

Routes for pages like this will probably be in the form /entity-name/entity-id and Next's dynamic routes work really really well since you can do things like router.push('/customers/[customerId]', '/customers/baer'). There is still a catch. If you plan to serve these files statically with something like Serve, Netlify, NGINX, etc, you'll need to generate a set of redirects so that users won't get a 404 on page-refresh and, to do that, you still need exportPathMap.

The following is copied, almost as-is, from a codebase I work on regularly:

const buildServeConfig = redirects => {
  const config = {
    public: `dist`,
    trailingSlash: true,
    rewrites: redirects
  };

  const outputPath = `${__dirname}/serve.json`;

  fs.writeFile(outputPath, JSON.stringify(config, null, 2), err => {
    if (err) {
      throw err;
    }
    // eslint-disable-next-line no-console
    console.log(`Generated: ${outputPath}`);
  });
};

...

exportPathMap: function(defaultPathMap, { dev, outDir }) {
  const redirects = Object.entries(defaultPathMap)
    // No need to create a redirect rule for `/dirname/` or `/dirname/index.html`
    .filter(([url]) => url !== `/` && url !== `/index`)
    .map(([url, { page }]) => ({
      // Replaces /[customerId] with /:customerId
      source: url.replace(/]/g, ``).replace(/\[/g, `:`),
      destination: `${page}/index.html`
    }));

  // By default, the routes are sorted such that a route like `/order/:orderId`
  // comes before `/order/new`. Since the `:orderId` portion of `/order/:orderId` 
  // is a wildcard, the route `/order/new` will be a match and consider `new` 
  // as a value for `:orderId`. To get past this, we sort the redirects by the 
  // number of parameters in ascending order.
  const sortedRedirects = [...redirects].sort(
    (currentRedirect, nextRedirect) =>
      currentRedirect.source.split(`:`).length >
      nextRedirect.source.split(`:`).length
  );

  buildServeConfig(sortedRedirects);

  return defaultPathMap;
}

I understand that this RFC does not deprecate or remove any APIs and I also recognize that it is possible to build these redirects by traversing the build directory so even if it were deprecated, I've got a nice ecape hatch. But, it doesn't quite "remove the need for getStaticPaths."

Again, thank you for your thoughtfulness in how you run this project

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 26, 2019

Are getStaticProps/getStaticPaths and getServerProps mutually exclusive? i.e. would it be possible to have a part prerendered and a part dynamic at the same time?

Yeah they are as one is static generation and one is server-side rendering.

@jkjustjoshing

This comment has been minimized.

Copy link

@jkjustjoshing jkjustjoshing commented Nov 26, 2019

This fixes one of the big things I miss from Gatsby before we migrated to Next:

We have a monolithic (100s of kbs) JSON file that we pull data from to render our pages that never changes. In Gatsby we loaded the JSON file into the GraphQL schema and queried against that, only grabbing the data that we needed to render a given page. With Next, the easiest/cleanest way we found to do this is import monolith from './monolith.json', which requires the user download the entire JSON file.

This RFC 100% addresses this use case and brings Next one step closer to being on-par with Gatsby in the areas Gatsby shines (obviously, Gatsby can't do runtime SSR, so I'm only talking about static buildtime renders)

@grushetsky

This comment has been minimized.

Copy link
Contributor

@grushetsky grushetsky commented Nov 28, 2019

@timneutkens, thank you for the RFC!

I have a use case for Next.js that I recently discussed with @rauchg.

Next.js delivers very smooth DX and some reasonable defaults. So, I'm interested in using Next.js for a client-side only rendered application, a Smart TV app.

Smart TV apps are almost classic web apps that are run by TV’s browser engine:

  1. The app is packaged into a bundle: styles, scripts, images, index.html, certificates and TV config file.
  2. The bundle is submitted for review to platform’s app store.
  3. The bundle is then installed from the store as an app and is run by the user.

The thing is that the bundle is statically hosted by the TV device itself and not loaded from the server. Thus, no SSR option is possible (Node.js isn’t exposed to developers for these purposes). But the app itself is dynamic (say, Netflix).

So, we need to run an SPA that is hosted by a static web server.



As I understand opting-out of getServerProps (or getInitialProps) completely will help to avoid SSR. But what happens with dynamic rendering on the client? And what about routing in this case? According to this RFC the problem hasn’t been addressed yet. @timneutkens, could you, please, suggest the best way to enable client-side only rendering in Next.js? And whether it fits Next.js in the first place? Thanks!

P.S. I can create an issue for this use case if you think it's better to discuss it separately.

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Nov 28, 2019

@grushetsky can you create a different issue. This is a completely different question from what is being discussed in the RFC 👍

@outdooricon

This comment has been minimized.

Copy link

@outdooricon outdooricon commented Dec 3, 2019

@timneutkens The promise of this RFC is one of the things that got me super excited about Next! Just to clarify though, getInitialProps would still exist too, right?

@Timer

This comment has been minimized.

Copy link
Member

@Timer Timer commented Dec 3, 2019

Correct @outdooricon -- getInitialProps will remain around for the foreseeable future.

Per the RFC:

This RFC exclusively discusses API additions. All new functionality is completely backwards compatible and can be incrementally adopted. This RFC introduces no deprecations.

@Ephem

This comment has been minimized.

Copy link
Contributor

@Ephem Ephem commented Dec 4, 2019

Great RFC, super excited for this!

I've been thinking about the getServerProps in relation to a specific use case, putting the results in a cache. Since this gets turned into a API-endpoint and the result delivered to the component as props, is there a prescribed way of putting the result into an external cache like Redux, GraphQL-caches etc, etc client side?

If I understand getInitialProps correctly, since it's static and async, Next has the opportunity to wait for it to complete before ever rendering the component a first time. This lets us put things into an external cache there. This won't be the case with getServerProps since it runs on the server, and putting things in a cache in the component lifecycle seems to mean we have to have a render where the data is not available in the cache yet, even if it is available in props?

This might be intentional of course and I might be missing an approach, but I thought I'd ask if it's something that's been considered?

Edit: I guess this also applies to getStaticProps. 😄

@homoky

This comment has been minimized.

Copy link

@homoky homoky commented Dec 5, 2019

Maybe I've missed it somewhere, but how do we handle situations when content is cached, but it is updated in db or new blog post is created? There is need to do new build automatically? I guess so.

@reaktivo

This comment has been minimized.

Copy link

@reaktivo reaktivo commented Dec 6, 2019

First of all! Great proposal, it's a massive improvement over exportPathMaps on most people's use case. It's really appreciated. With that said, I'm struggling to understand how would we will be able to make it work with route internationalisation.

Is there any suggestions on how to handle i18n prefixed routes? My specific use case requires to build a few thousands on pages with different country-lang prefixes and urls.

/nl/brillen
/gb/glasses
/es/gafas
...

It seems that getStaticPaths will be really helpful when the prefix for the url is well known, as in your example (using /blog/[id].js). But how do you think a getStaticPaths implementation will look if it needs to generate paths at root level, with both a dynamic prefix (country-lang) and a dynamic path?

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Dec 7, 2019

@reaktivo pages/[lang]/blog/[id].js -> in getStaticPaths provide all urls to statically render.

@flawyte

This comment has been minimized.

Copy link

@flawyte flawyte commented Dec 7, 2019

@timneutkens Any idea when this will be available / testable?

@timneutkens

This comment has been minimized.

Copy link
Member Author

@timneutkens timneutkens commented Dec 8, 2019

In general we don't give out ETAs because we extensively test features against production apps to make sure the solution is right.

@MoOx

This comment has been minimized.

Copy link

@MoOx MoOx commented Dec 12, 2019

This improvements will totally make me deprecate my "not that maintained" phenomic project (react ssg that nobody uses except me). Awesome to see Next.js adding this missings parts!

@brunomarchioro

This comment has been minimized.

Copy link

@brunomarchioro brunomarchioro commented Dec 12, 2019

I would like to clarify a doubt. Considering the use of a CMS like wordpress. As I understand it, with the getStaticPaths method I would fetch all posts and pass a list like:

export async function getStaticPaths () {
  return [
    // This renders / blog / hello-world to HTML at build time
    {params: {slug: "hello-world"}}
  ];
}

The slug of each post would be used in the getStaticProps method to fetch the content.
This would happen in npm build.
My question is about new posts that will be added after the build.
Would getStaticProps method be used to fetch this new post by slug?
Will this new post have a .html file like the one in the previous build?
I love to work with next and in several projects I have this would be very good.

@ScreamZ

This comment has been minimized.

Copy link

@ScreamZ ScreamZ commented Dec 13, 2019

Nothing directly related, but the support is unable to give me an answer that matches my question.

What you suggest here could be the solution, but in the meantime, I'm unable to make nextJS to build a JAMSTACK based on webhooks changes.

if I had getInitialProps I'm going to be server-rendered.
If I don't, I'm just CDNized, but without pre-rendering isn't? And the page will be without content as long as XHR haven't returned (bye-bye SEO)

Do you have any running example with now of Jamstack with nextJS and we could do on netlify.

Thanks,
Andréas

@jescalan

This comment has been minimized.

Copy link

@jescalan jescalan commented Dec 13, 2019

Hey @ScreamZ - this change is, I think, what enables a fully static site to be built with nextjs. We have been able to compile out a nextjs site to static using next export for a long time, but it would still fetch data on client-side route transitions using getInitialProps. With the ability to use getStaticProps, you can run client-side transitions without any additional data being fetched -- all fetched data in getStaticProps is fetched once, at build time, and not updated on your live site unless you rebuild again. This is the classic architecture of data-driven static sites, link your data source to your host via webhook and when the data source changes, you tell the host to rebuild your site.

There are plenty of existing examples of fully static nextjs websites, and its trivial to run a nextjs site on netlify. My company's website is currently running on nextjs, and hosted by netlify, hopefully this serves as a good example.

It's very much worth noting that zeit's hosting service is also something worth being strongly considered. The pricing is quite similar, and their integration with nextjs sites is second to none - you literally don't even need to configure anything at all, you just link github and zeit's hosting will recognize that you're running nextjs and automatically configure and deploy everything.

This is not an advertisement by any means, I do not work for zeit, just a genuine endorsement. You can absolutely make it work with netlify, and I have personally for several sites as proof. But you will need to understand thoroughly how nextjs works, and you will need to make sure everything is configured correctly to get it running smoothly on netlify. If you're looking for the simplest, most foolproof hosting for a nextjs site, I would give zeit's hosting a try.

@ScreamZ

This comment has been minimized.

Copy link

@ScreamZ ScreamZ commented Dec 15, 2019

@jescalan Thank you for this great sharing 🙏🏻

I've no issue using NextJS with netlify, because you can use Publish directory to specify the out folder. But on zeit Now, there is no way to say, please don't use SSR but go full on static with next export.

@jescalan

This comment has been minimized.

Copy link

@jescalan jescalan commented Dec 16, 2019

@ScreamZ this is sort of true, but it depends on how exactly you define a "full static" site. If you use getStaticProps for all your pages with zeit's hosting service, what you will get is effectively equal to a static site, even if it doesn't run next export, since all pages with getStaticProps are built only when the site is deployed, and are served directly from the CDN after that.

The main difference is that as far as I know there isn't a way to force all pages to be static on zeit's hosting. Pages with getStaticProps behave exactly the same as pages generated by next export -- a single static copy of the page is served directly from the CDN on every hit. But you could also run some pages with getServerProps or getInitialProps and they would behave as server rendered pages. Personally I see this as a benefit -- if there's a need for a SSR route you can simply use a different data fetching method and that single route is now SSR, while all your other routes can remain static.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.