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

Support static site rendering #70

Closed
dlindenkreuz opened this issue Oct 25, 2016 · 49 comments
Closed

Support static site rendering #70

dlindenkreuz opened this issue Oct 25, 2016 · 49 comments

Comments

@dlindenkreuz
Copy link
Contributor

Looking at the overall project structure, this seems like a rather easy step on your roadmap. Maybe someone from the team can provide a good starting point for contributors?

@impronunciable
Copy link
Contributor

Hi @dlindenkreuz what do you mean with static site rendering?

@ejulen
Copy link

ejulen commented Oct 25, 2016

@impronunciable I assume @dlindenkreuz means rendering an application written with Next to static HTML that could be served up without a backend.

@sebastianmacias
Copy link

sebastianmacias commented Oct 26, 2016

Static site rendering would allow to save the output of all pages once and host it in S3 or any other static hosts without the need of running a server/node.js see: https://www.staticgen.com/

Would definitely be a huge plus to have.

@dlindenkreuz
Copy link
Contributor Author

Exactly @ejulen @sebastianmacias: a statically rendered website would eliminate the need for a Node server.

This is certainly only possible in some scenarios e.g. without dynamic routes or user input processing but would allow things like Github Pages hosting.

@tiye
Copy link

tiye commented Oct 26, 2016

For dynamic routes, it's possible if you name the paths like a.html. I'm not sure if every router library handles it correctly, but it works as I tried with my tools.

@dlindenkreuz
Copy link
Contributor Author

@jiyinyiyong Can you elaborate on that? I checked your examples and don't understand the relation to this issue in particular.

@tiye
Copy link

tiye commented Oct 26, 2016

@dlindenkreuz I was trying to explain that dynamic router will still work when the app is built into static files. For example in this demo:

http://vue-pages-workflow.mvc-works.org/
https://github.com/ElemeFE/vue-pages-workflow/blob/master/configs/task-render.js#L28-L51

There is also an about.html page. After you navigate to that page and reload, it works as normal. The reason is there is .html suffixed in the path, it's part of the path. So dynamic router will work as long as the router allows .html in the path.

There are also more demos, well, I didn't try react-router or the router provided by next.js , but I'am sure it works in theory since I tried several times:

And it's true not all routes can work perfectly, for example /a/:b or /:b/a while b can not be enumerated, then we can not prepare all of them. If your "dynamic router" referred to this case, well, that's what I mean.

@lpil
Copy link

lpil commented Oct 26, 2016

I'd also like this. The problem with conventional static sites is that the
compilation process is slow yup work with in dev, however if in dev you
work on a browser based react app served from webpack dev server this
problem is removed. It'd be nice if you'd project could make this pattern
easier and more accessible.

Cheers,
Louis

On Wed, 26 Oct 2016, 04:15 Sebastian Macias, notifications@github.com
wrote:

Static site rendering would allow to save the output of all pages once and
host it in S3 or any other static hosts without the need of running a
server/node.js https://www.staticgen.com/ would definitely be a huge plus
to have.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#70 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AF2ahmuopctpn4Nycz0Ee0aQTRUmRTqfks5q3sXpgaJpZM4KgfZ0
.

@dlindenkreuz
Copy link
Contributor Author

@jiyinyiyong Ah, thanks for the explanation! Indeed, the page tree would need to be fixed and determinable at the time of compilation. Handling suffixes like .html should be left up to the server used for hosting the compiled site, although I'd love to customize the file name extension of generated files at build time.

Figuring out that tree should probably happen automatically in regards to the Next.js "spirit" of dropping components in /pages to have them render as a page.

@lpil You might want to check out Gatsby.js until this is available in Next.js 😉

@altaywtf
Copy link

Hi guys, I wonder how this feature will affect the usage of getInitialProps method.

Doesn't it give us to opportunity of prefetching data on the server side? (like async-props and redux-connect)

@dlindenkreuz
Copy link
Contributor Author

dlindenkreuz commented Oct 28, 2016

@altayaydemir Yes, but the thing is that some projects do not need or can not have a dynamic backend (like GitHub Pages). In these cases, a bunch of static HTML files and directories are created at compile time.

Prefetching data e.g. from JSON files can still happen at compile time but won't happen again when opening a page, because the page is then just a standalone HTML file. After you compile once, no Node.js server is involved anymore.

@lucasconstantino
Copy link
Contributor

This would definitely be a huge plus for me. I started to migrate a static website to Next just to notice it's "build" command didn't really built an static structure :(

@altaywtf
Copy link

@dlindenkreuz ok, I get it. I thought I've missed something about the getInitialProps part.

I've made a similar config to one of my active projects (with webpack / react-router code splitting) and I totally agree with you about this.

@acamarata
Copy link

This would definitely be a huge plus for me. I wanted to use Next.js for a couple specific projects but front-end code must be statically hosted and then using GraphQL and API's with a back-end for anything that must be needed. Static Hosting can still use JS so Webpack, Babel, React, Router, Redux, etc. are all still on the table but it has to be purely "front-end" with anything back-end done as a request (or as we will be doing in our app an Apollo/GraphQL subscribe for live-data to a separate back-end). Next.js seemed unable to do this so we went with create-react-app and built things the harder way... would like to know if this is an idea liked by the core team and will happen.

Note: since I do a lot of static sites just some solid advice.. anyone considering GitHub Pages should really look at Netlify.com instead as they do not limit plugins, do continuous deployment to a GitHub repo (push triggers deploy), allows you to state your build command, env vars, outpur dir, etc. as well as give custom domain and SSL in the free tier. (They also have free Pro tier for open-source projects).

@acamarata
Copy link

acamarata commented Nov 25, 2016

Just noticed the "Feature Request: build mode which outputs all html files #162" and I would suggest that this can be done more in a SPA (Single Page App), PWA (Progressive Web App) using Service Workers, offline support, etc. if it is enabled to do "front-end only" builds... no need for "static html only files" unless that was also wanted. I think SPA/PWA/SW with router and "front-end only" is easier and not a big change from a standard Next.js just takes detaching the back-end and making it something "plug and playable" by services like Scaphold.io and others that can give you a back-end as a service with database and auth api's.

(my 2 cents)

@sylvainbannier
Copy link

Isn't that "just" a matter of recusive wget ?

@jferrettiboke
Copy link
Contributor

As an alternative, I tried to get the static content using wget. Although it works with other of my projects (without using next), it does not work well here with next.

I use wget and everything is OK, except the links. If I inspect the links, there is no href attribute for any local link. Even though there is no href attribute when I inspect the link, after compiling the app to static files and clicking the link, it tries to open the slug name plus the ".json" extension. So, as result, I get a 404 error in my terminal saying: "GET /about.json" Error (404): "Not found".

Does anyone of you know the reason of this? Could it be a conflict with the Link component?

Here the commands that I use with wget.

wget --html-extension --recursive --convert-links --page-requisites --no-parent --directory-prefix dist --no-host-directories --restrict-file-names=unix http://localhost:3000/

@matthewmueller
Copy link
Contributor

matthewmueller commented Nov 30, 2016

I'm definitely +1 to this feature, but in gathering my thoughts on this topic, I've come to some conclusions that I think are worth pointing out:

All the static hosting services mentioned have a server in the backend that is doing more than just serving static files – they provide a routing layer, handle caching, etc.

The problem next.js will run into trying to support this is the dynamic nature of getInitialProps.

So what we really want is either next.js to work on more platforms (Github Pages, Netlify, Surge, S3 + Cloudfront) or for these platforms to become more capable like Heroku, Dokku, Now, EC2.

If we focus on making next.js work on more platforms, I don't think a truly dynamic getInitialProps is the solution but there may be some other slightly less dynamic flavor we could adopt like create pages from your data at build time, something that would be great for building blogs, but not so great for building a social network.

@jonaswindey
Copy link
Contributor

For static pages we'll also have to switch to hash history instead of html5 browser history (so url's use #/path instead of /path since I think most static hosting don't support url rewritings.

@sylvainbannier
Copy link

I may be wrong but if you use client side routing, what's the point of having next.is ?
There's others react based static generator around.

@foxyblocks
Copy link

@jonaswindey

For static pages we'll also have to switch to hash history instead of html5 browser history (so url's use #/path instead of /path since I think most static hosting don't support url rewritings.

Why would you need URL rewriting?

@acamarata
Copy link

acamarata commented Dec 4, 2016 via email

@matthewmueller
Copy link
Contributor

There's definitely a use case for both, but what's missing right now from next.js is generating HTML pages that only require the JS that they need (code-split across pages).

A SPA with client-side routing is quite a bit simpler and could probably be done without next.js

@jferrettiboke
Copy link
Contributor

jferrettiboke commented Dec 5, 2016

Give a try to next export.

@pex
Copy link
Contributor

pex commented Dec 5, 2016

@jferrettiboke I don't think so. Where did you read about an export feature?

@jferrettiboke
Copy link
Contributor

Read here, @pex. Although, at this point in time, it does not work properly for me. I opened #348 to go further. I think, there is no documentation about this.

@ejulen
Copy link

ejulen commented Dec 6, 2016

@jferrettiboke I think you misunderstood Guillermo Rauch. Pretty sure he was saying he wanted static site generation, too, and gave an example of what that command could look like.

@jferrettiboke
Copy link
Contributor

Thanks @ejulen. I think you are right. I had a misunderstood. Sorry for the inconvenience.

@MoOx
Copy link
Contributor

MoOx commented Dec 8, 2016

@rauchg any comment about your "export" idea?

@rauchg
Copy link
Member

rauchg commented Dec 8, 2016

Basically next export will output all the pages/ into a static directory as .html. getInitialProps gets invoked on the client exclusively, including on first render. That's it!

@MoOx
Copy link
Contributor

MoOx commented Dec 8, 2016

So we won't be able to handle dynamic routes? For example let's say I want dynamic pages to list pages with a given content (eg: I have a collection of blog posts and want to have pages for each tags - /tag/{a tag}), there won't be a way to support that?
That would be nice to be able to have a way to add url to render via a callback or something.

@flybayer
Copy link
Contributor

flybayer commented Dec 8, 2016

Could this somehow support the prefetch Link component, so we can preserve instentanious page transitions on the client?

@rauchg
Copy link
Member

rauchg commented Dec 8, 2016

@MoOx I hadn't considered that. I think we should absolutely contemplate that
@flybayer absolutely. I don't see why client-only mode would lose that.

@MoOx
Copy link
Contributor

MoOx commented Dec 9, 2016

For now here is what I do to allow people to generate whatever routes they want:

  1. I consume people react-router routes
  <Route component={ AppContainer }>
    <Route path="showcase" component={ Showcase } />
    <Route path="showcase/tag/:showcaseTag" component={ Showcase } />
    <Route path="*" component={ DocsPageContainer } />
</Route>
  1. I create all possible routes by playing with all :params. This part may be a bit specific in my case: I read all possible values by browsing a collection of data.
    To be more specific: people most of the time pass markdown files, and I read the entire front-matter to build a index - this allows me to have all possible values for random keys - eg: authors, tags - in my previous routes showcaseTag ).
    I guess we could find a more elegant way to allows people to define some parameters that can be used to resolve routes?

  2. At this point I have an array with all URLs to render, so the next step is easy: just iterate on all urls and render those.

This thing will not work (or with a lots of limitation) for me at some point since react-router v4 API is totally different and people can defined recursive urls by creating small piece of urls in any components. That's a good idea but it's just not really compatible with my approach (or I will need to tell people that there are many limitation like "hey you can use react-router v4 but not really as you need to explicitly pass a bundle of routes and nested routes will be ignored").

From my knowledge of next, there is no router (fs is the router) and the only way to have dynamic urls is using the programmatic API.
If I am correct I guess at some point we should maybe only allow a "thing" to be called before trying to only export urls based on ./pages directory.

next export will probably need to read all ./pages path, so I guess we should open this step somehow to people so we can add some logic (or replace it?!) so they can add anything they want. Maybe it's too dangerous (we don't want people to generate 404 for a bunch of urls, do we?)

For now I cannot think of another way: since there is no router with routes defined, we cannot really guess what value to map.


Another thing to think about is "redirection". It will be nice to be able to define urls to generate redirect code like jekyll-redirect does (tldr: you just output a meta refresh tag with the new url - it's not very clean but works and is considered as 301 by google (I asked on a google forum, I get an answer for a google engineer that said "lots of people still use that so we have to")

@rauchg
Copy link
Member

rauchg commented Dec 9, 2016

@MoOx it's still possible that for the purpose of next export specifically we can supply a route map

@MoOx
Copy link
Contributor

MoOx commented Dec 9, 2016

That would be awesome and will offer full control to developers :)

@rauchg
Copy link
Member

rauchg commented Dec 9, 2016 via email

@MoOx
Copy link
Contributor

MoOx commented Dec 9, 2016 via email

@MoOx
Copy link
Contributor

MoOx commented Dec 12, 2016

I think at some point, programmatic API and "no routes" will never be enough if you want to support stuff like pagination.
Let's say I want to have /tag/{tag}/page/{page}. You will need to be able to statically know how many tags and page for each tags you will need before you even render anything.
With the current new API I plan to introduce for phenomic (in short HoC that you use for components specified in your routes) it will be easy to get those params but that will only works with a static check so we can "compute" all routes to render.

@rauchg
Copy link
Member

rauchg commented Dec 24, 2016

@MoOx yep i've kept thinking about this, and for exporting statically, a route map option (via JSON for example) will be pretty interesting.

@matthewmueller
Copy link
Contributor

matthewmueller commented Dec 27, 2016

FWIW, i've done this successfully on the last project i worked on:

"routes": {
  "/": "/landing",
  "/signup": "/signup",
  "/signup/*": "/signup",
  "/login": "/login",
  "/me/edit/password": "/me-edit-pass",
  "/me/edit/bio": "/me-edit-bio",
  "/article/manual": "/article-manual",
  "/article/manual/*": "/article-manual",
  "/article/auto": "/article-auto",
  "/about": "/about",
  "/404": "/404",
  "/access-restricted": "/access-restricted",
  "/privacy": "/privacy",
  "/tos": "/tos",
  "/imprint": "/imprint",
  "/:author": "/profile",
  "/:author/:article": "/profile"
}

Allowing static pages with dynamic routing. This was in the package.json, but since there's a next.config.js, it could go in there.

@rauchg
Copy link
Member

rauchg commented Dec 27, 2016

Very cool!

@intermundos
Copy link

@matthewmueller can you please provide more information on routing map? Thanks.

@stretchkennedy
Copy link

Building on @jferrettiboke's wget, I've been using

#!/bin/bash -e

ADDR=${ADDR:-'http://localhost:3000'}
DEST=${DEST:-'dist'}
PAGES_DIR=${PAGES_DIR:-'pages'}

echo "starting server..."
next build
next start 2>&1 > /dev/null &

echo "downloading static assets..."
wget --html-extension \
     --recursive \
     --convert-links \
     --page-requisites \
     --no-parent \
     --directory-prefix "$DEST" \
     --no-host-directories \
     --restrict-file-names=unix \
     --quiet \
     --retry-connrefused \
     --waitretry 2 \
     --tries 5 \
     -i <(find "$PAGES_DIR" -type f | \
                 sed "s:^$PAGES_DIR\(.*\)\.js\$:\1:g" | \
                 sed 's:/index$:/:g' | \
                 sed "s,\(.*\),$ADDR\1,g")

echo "closing server..."
pkill -INT -g $$ node
wait

echo "done"

@rauchg
Copy link
Member

rauchg commented Jan 8, 2017

Pretty impressive! Hopefully next export will make that a breeze :)

@rauchg
Copy link
Member

rauchg commented Jan 8, 2017

We'll move the discussion from here to #604

@rauchg rauchg closed this as completed Jan 8, 2017
@Maxhodges
Copy link

Maxhodges commented Jan 14, 2017

@matthewmueller

.All the static hosting services mentioned have a server in the backend that is doing more than just serving static files – they provide a routing layer, handle caching, etc.****

That's not quite right. There's no server-side router. Caching is prepared at build time then the site is literally served over CDN. What "etc."?

So what we really want is either next.js to work on more platforms (Github Pages, Netlify, Surge, S3 + Cloudfront) or for these platforms to become more capable like Heroku, Dokku, Now, EC2.

What we want want is to build react apps with client-side (JS) routing to work as static sites. I think you're confused by the word "static"--which is not uncommon. Static sites can do a lot of dynamic things thanks to client-side JS scripts and third-party APIs. . But the "server" only does the same thing every time: it runs a build process once per atomic build. Nothing more.

"Netlify is a build tool with CDN hosting--having that become like Heroku is not what we what. That would miss the whole point of static sites.

@matthewmueller
Copy link
Contributor

@Maxhodges take a look at https://www.netlify.com/docs/redirects/. At some layer in their stack there is dynamic server-side routing. The difference is that either we are managing that infrastructure or they are. Of course I'd love next.js to be managed by them.


In other news, if anyone would find this useful, I created a standalone module for doing what I described here: #70 (comment)

https://github.com/matthewmueller/next-route

Right now this is for the server, but I'm hopeful that once next has static builds, this will be the format of the routing.

@Maxhodges
Copy link

Maxhodges commented Jan 17, 2017

Redirects aren't really what I'd consider application routing. Redirects transform the URL before the router creates a router state out of it.

we are going with gatsbyjs

@lock lock bot locked as resolved and limited conversation to collaborators Jan 23, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests