Truly static pages without react app #32217
Replies: 46 comments
-
I think you can do that with a custom // pages/_document.js
import Document, { Main, NextScript } from 'next/document'
const pagesWithoutReact = ['/'];
export default class MyDocument extends Document {
render() {
const { __NEXT_DATA__ } = this.props;
return (
<html>
<body>
<Main />
{!pagesWithoutReact.includes(__NEXT_DATA__.pathname) ? <NextScript /> : null}
</body>
</html>
)
}
} |
Beta Was this translation helpful? Give feedback.
-
@danielr18 that's not a reliable way to remove the script tags as they're still preloaded. |
Beta Was this translation helpful? Give feedback.
-
Technically looks like that would do the job. Discovered Now facing another issue not directly related to next.js itself. The static page i'm trying to render is a snapshot of quite complex page currently rendered by Wordpress. If i navigate to this page directly (with full page reload) everything is smooth. But navigation using Link breaks the scripts already present on the page (there's angular, jquery and whole galaxy of other trash). Looks like something to do with scripts load order when they're added to the body dynamically. Continue with this - would rather have some neat solution in place before creating PR. |
Beta Was this translation helpful? Give feedback.
-
This would be a great addition to this ecosystem. We are looking to generate a number of static landing pages as lightweight entry points into our SPA. This is the perfect solution. |
Beta Was this translation helpful? Give feedback.
-
Whats the status of this? I'm needing something like this too. |
Beta Was this translation helpful? Give feedback.
-
Curious about the state of this, as I'm also looking to implement something similar for statically exported error pages. |
Beta Was this translation helpful? Give feedback.
-
Is there any other tool available to do this now? I too need something similar. |
Beta Was this translation helpful? Give feedback.
-
I did it as danielr18 described. Create a custom |
Beta Was this translation helpful? Give feedback.
-
I also did something similar to @danielr18. The problem with his example is that JavaScript files are still preloaded even though they are not needed. I extended // pages/_document.js
import Document, { Main, Head } from 'next/document';
class CustomHead extends Head {
render() {
const res = super.render();
function transform(node) {
// remove all link preloads
if (node && node.type === 'link' && node.props && node.props.rel === 'preload') {
return null;
}
if (node && node.props && node.props.children) {
return {
...node,
props: {
...node.props,
children: node.props.children.map(transform),
},
};
}
if (Array.isArray(node)) {
return node.map(transform);
}
return node;
}
return transform(res);
}
}
class StaticDocument extends Document {
render() {
return (
<html>
<CustomHead />
<body>
<Main />
</body>
</html>
)
}
}
export default process.env.NODE_ENV === 'production' ? StaticDocument : Document; This generates a minimal build without any JavaScript when running |
Beta Was this translation helpful? Give feedback.
-
@bancek had a small issue with your solution, had to change:
To:
|
Beta Was this translation helpful? Give feedback.
-
Super applying is Email templates embed in ecosystem, not as a side feature |
Beta Was this translation helpful? Give feedback.
-
@askirmas can you elaborate? |
Beta Was this translation helpful? Give feedback.
-
@iamstarkov In autumn |
Beta Was this translation helpful? Give feedback.
-
@askirmas i meant that i dont understand you |
Beta Was this translation helpful? Give feedback.
-
Example
export default ({children}) => <><Header/>{children}<Footer/></>
export default ({children}) => <>
<EmailHeader/>
<div>Dear {full_name}</div>
{children}
<EmailFooter/>
</>
export default () =><b>New Super Proposition!</b> With
There are many limitations to send |
Beta Was this translation helpful? Give feedback.
-
If somebody is interested, I've built a plugin that allows to add vanilla JS entry points for pages that have Note that this plugin modifies webpack configuration of Next.js. Similar as with other Next.js plugins that do this, it's possible that this plugin will break when there are updates to Next.js. I'm keeping the plugin updated so that it continues to work with new versions of Next.js. I've just updated the plugin to work with Next.js 9.5 🙂 . |
Beta Was this translation helpful? Give feedback.
-
A neater way of doing this (pruning out the script tags via a custom Have some logic in a custom node and serving this for paths as necessary That also has the advantage of leaving your site completely untouched for normal paths which need javascript interactivity I am doing this for a side project I am working on and it's working nicely |
Beta Was this translation helpful? Give feedback.
-
@artcg do you example of that |
Beta Was this translation helpful? Give feedback.
-
Sure, I won't include all the boilerplate etc that you would include but the pattern is simply // server.js
const {createServer} = require('http');
const {parse} = require('url');
const next = require('next');
const app = next({dev: process.env.NODE_ENV !== 'production'});
const handle = app.getRequestHandler();
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true);
if (parsedUrl.pathname === '/static_example') {
yourOwnCustomHandler(req, res);
// render your React Component to static markup here,
// add HTTP headers, and write to res, then call res.end()
} else {
handle(req, res, parsedUrl);
}
}).listen(8080, err => {
if (err) throw err;
});
}); The result is effectively your entire site is running with NextJS, except for whichever route you define which you can handle yourself It's just node so it is fully controllable :) |
Beta Was this translation helpful? Give feedback.
-
This solutions makes you opt out of all Next.js features because you opt-out of using Next.js completely. Would recommend just using #11949. |
Beta Was this translation helpful? Give feedback.
-
I tried that but it didn't work, and there is no documentation for it anywhere, for my case it was easier to opt out of next for that file |
Beta Was this translation helpful? Give feedback.
-
Isn't the following solve this issue?
(taken from #11949) |
Beta Was this translation helpful? Give feedback.
-
Only partially. See #11949 (comment) |
Beta Was this translation helpful? Give feedback.
-
Having truly static pages is something I want as well. I ended up doing it post-export since I already had some hooks there, but having it natively would be nicer. I am looking forward to https://twitter.com/shaneOsbourne/status/1360708099552780293 being released :) |
Beta Was this translation helpful? Give feedback.
-
My usecase is similar to those that have mentioned "email templates." Email templates really shouldn't have any JS in them at all, so having this ability would really open doors for my team. Right now I am planning on just injecting the template into an iframe to support this usecase. Here's what I'm doing: import { EmailTemplateContextUiProvider } from '@ui/contexts/email-template-context';
import React, { FC, ReactChild, ReactChildren } from 'react';
import Frame from 'react-frame-component';
export interface EmailTemplateBaseProps {
shouldRenderIframeWrapper?: boolean;
shouldRenderHtmlAndBody?: boolean;
shouldRenderNextPage?: boolean;
title?: string;
head?: ReactChildren | ReactChild;
}
export const EmailTemplateBase: FC<EmailTemplateBaseProps> = ({
children,
shouldRenderNextPage = true,
shouldRenderIframeWrapper = shouldRenderNextPage,
shouldRenderHtmlAndBody = shouldRenderIframeWrapper,
title = 'My Email Template',
}) => {
let newChildren = (
<EmailTemplateContextUiProvider>
{children}
</EmailTemplateContextUiProvider>
);
// Optionally render template inside a full HTML document, otherwise
// render the template's body only
if (shouldRenderHtmlAndBody) {
newChildren = (
<html>
<head>
<title>{title}</title>
</head>
<body>{children}</body>
</html>
);
}
// Optionally render template inside an <iframe /> to avoid any JS from Next.
// When NextJS supports fully static pages, we can rethink this: https://github.com/vercel/next.js/issues/5054#issuecomment-805829676
if (shouldRenderIframeWrapper) {
newChildren = (
<Frame
style={{
width: '100%',
height: '100%',
border: 0,
flexGrow: 1,
padding: 0,
margin: 0,
}}
>
{children}
</Frame>
);
}
return newChildren;
}; |
Beta Was this translation helpful? Give feedback.
-
Just figured I need some embeddable component for an iframe for one page, pretty much like Twitter embeddable card or something. I would like to have some js in there though, so react hooks etc, - I want that, but I wanna disable all the nextjs features like any kind of preloading, routing, etc. Is it possible to disable all that just for one page? Or should I create-next-app inside another nextjs app to isolate this little page and it's the only way currently? |
Beta Was this translation helpful? Give feedback.
-
In my case, I'm still using the workaround that @bancek proposed above because the The problem I recently encountered is when using the new Image component, that the |
Beta Was this translation helpful? Give feedback.
-
I think this is worth the effort. I was tempted to use astro just because of this. But i prefer Next by far so im kinda stuck with either full React or only basic self made html css build tools with webpack. I even tried Hugo but it was way overcomplicated. |
Beta Was this translation helpful? Give feedback.
-
Now with Next.js 12 and the alpha integration with Server Components, will this issue have a proper solution?
I was waiting for the React Server Components (RSC) to land hoping that they would trivially solve this issue since they could run once during compile time, and then the runtime could just include the client component along with the static version of the server components embedded (if needed at all). However, the Server Components alpha documentation, https://nextjs.org/docs/advanced-features/react-18#unsupported-nextjs-apis, mentions:
So this feels that they are only usable when you actually have a server running, and not integrated with Is this something due to being Thanks for all the amazing work so far! |
Beta Was this translation helpful? Give feedback.
-
Slightly updated version of #32217 (comment) with the fix from #32217 (comment) and some optional chaining: // pages/_document.ts
import { Head, Html, Main, NextScript } from "next/document";
import config from "../next.config.mjs";
// FROM: https://github.com/vercel/next.js/discussions/32217#discussioncomment-1766291
function transform(node) {
if (node?.type === "link" && node?.props.rel === "preload") {
return null;
}
if (node?.props?.children) {
return {
...node,
"props": {
...node.props,
"children": Array.isArray(node.props.children) ? node.props.children.map(transform) : transform(node.props.children)
}
};
}
if (Array.isArray(node)) {
return node.map(transform);
}
return node;
}
class CustomHead extends Head {
public override render() {
return transform(super.render());
}
}
export default function Document() {
return (
<Html lang="en">
<CustomHead>
<meta name="description" content="" />
<base href={config.basePath + "/"} />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true" />
<link href="css/fontawesome-5.15.4.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@300&display=swap" rel="stylesheet" />
</CustomHead>
<body>
<Main />
<NextScript />
</body>
</Html>
);
} |
Beta Was this translation helpful? Give feedback.
-
Feature request
Is your feature request related to a problem? Please describe.
We want some static pages to be as lightweight as possible and we're fine with full page reloads navigating out of them if we can chop off extra 200Kb.
Describe the solution you'd like
Add an option to path map in next.config.js to indicate this specific page doesn't need to include react app and can operate as plain HTML page.
React app is only removed in case the flag is set to
true
explicitly.Looks like it should be enough to just make
files
content empty there if option is set. https://github.com/zeit/next.js/blob/canary/server/render.js#L100For the simple example from tutorial setting up
As a result
/out
would be populated withThis way index page is going to be very lightweight, with a tradeoff of full page reload navigating to /about. While /about would load the app, so navigation back to / would be handled on the client-side.
Describe alternatives you've considered
Static site generators :) But we need way more than just static pages and next.js fits us best except for this tiny quirk.
Additional context
Happy to implement that, just wanted to make sure this is not something going against the roadmap for next.js. Or if it's something already available, but hidden. I'm new to the library, only playing around for the second day.
Beta Was this translation helpful? Give feedback.
All reactions