vercel / next.js

The React Framework
https://nextjs.org
MIT License
121.8k stars 26.05k forks source link

Static Generation / SSG Improvements #9524

Closed timneutkens closed 4 years ago

timneutkens commented 4 years ago

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.

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 getServerSideProps. 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.

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:

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

Fallback

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 fallback 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.

The exact behavior of serving will be:

In case you want paths that weren’t generated at build time to result in a 404 that is also possible by returning fallback: false from getStaticPaths

// `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: "hello-world" } }
    ]
  };
}

getServerSideProps

When using getServerSideProps, 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 getServerSideProps. 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 getServerSideProps 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

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

The context will contain:


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

maxchehab commented 4 years ago
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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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.

tomsseisums commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

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

flawyte commented 4 years ago

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 commented 4 years ago

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

Fixed!

jstcki commented 4 years ago

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 commented 4 years ago

@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.

jstcki commented 4 years ago

@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 commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

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.

jstcki commented 4 years ago

@jaredpalmer I assume https://github.com/zeit/next.js/issues/9524#issuecomment-558628066 (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 commented 4 years ago

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

baer commented 4 years ago

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:

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

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

outdooricon commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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

flawyte commented 4 years ago

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

timneutkens commented 4 years ago

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

MoOx commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

@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 (edit: zeit recently changed it so that any site with a config that contains exportPathMap will run a fully static site, so this is no longer true). 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.

ScreamZ commented 4 years ago

@jescalan Thanks,

So just need to wait to have this being implemented, in the meantime gonna use netlify for static

jacobrask commented 4 years ago

Is there a story around SSG configuration? Specifically we would like to use shared build artifacts but run next export with different configurations for QA/prod. These config values would only be read in getStaticProps. Would this use the serverRuntimeConfig or publicRuntimeConfig or process.env directly?

timneutkens commented 4 years ago

@ScreamZ @jescalan I've landed zero-config next export support on Now today together with @Timer (he deserves all the credits). You can do:

"build": "next build && next export"

And it'll work automatically.

Let me know how it goes 🙏

ScreamZ commented 4 years ago

Yeah, I was the guy that asked on support and they told me this has been implemented just yet 😅 As far as I can see, you'll need to define an export map in config?

timneutkens commented 4 years ago

@ScreamZ no you can just add next build && next export as shown above and it'll work.

coodoo commented 4 years ago

@timneutkens If I replace getInitialProps with getServerProps, do I still need to add target: 'serverless' to the config file to enable Server Pre Rendering? Thanks.

Regaron commented 4 years ago

How can we try these?

purecopy commented 4 years ago

How can we try these?

I guess all these methods currently need unstable_ prefix to get recognized.

e.g unstable_getStaticProps

ScreamZ commented 4 years ago

@timneutkens

@ScreamZ @jescalan I've landed zero-config next export support on Now today together with @Timer (he deserves all the credits). You can do:

"build": "next build && next export"

And it'll work automatically.

Let me know how it goes 🙏

My build script is doing a bit more things but it looks like it works like a charm:

"build": "graphql codegen && next build && npm run export",

Besides, it's great! It was exactly what I was looking for 😅 (Goodbye GatsbyJS, my favorite framework is now strong as you!)

Thanks a lot for such reactivity.

I also upgraded to 9.1.6 and I surprisely saw that

Screenshot 2019-12-21 at 19 25 43

I thought that thread was an RFC, it looks like it's already opened to us ahah, isn't ? However, Typescript types are not enabled in 9.1.6.

Damn, I'm so hyped for that now! 🤣

Last questions:

Thanks for that great tool 🙏🏻

timneutkens commented 4 years ago

If I get it, getInitialProps will be deprecated in the future? Or is it still relevant in some cases? An example?

As said in the initial RFC:

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

I thought that thread was an RFC, it looks like it's already opened to us ahah, isn't ?

It's not, we're trying it out on ZEIT applications and some of the visibility surfacing has already landed (for example the pages tree you saw).

next export can also be deprecated in favor of pages with getStaticProps and next build only?

Correct, in general you'll end up just not using next export. It'll be kept around for backwards compat reason but in general you'll want to create a hybrid app as it gives you all the benefits of export with support for other features like API routes and opting into server-side rendering for some pages.

timneutkens commented 4 years ago

How can we try these?

I guess all these methods currently need unstable_ prefix to get recognized.

e.g unstable_getStaticProps

Highly recommend not using it yet, it's experimental and can break between releases.