vite-ssg-react
v0.1.8
Published
`dev`, `build`, and `preview` scripts like Vite to generate static HTML websites from React/JSX
Readme
vite-ssg-react
Disclaimer
This repo is very much a work-in-progress at this stage. There are still bugs and things might break.
Commands
dev, build, and preview scripts like Vite to generate static HTML websites from React/JSX
npm run dev
Start an SSR dev-server with hot-reloading.
npm run build
Pre-render all JSX into HTML/CSS/JS & assets into the HTTP-ready folder dist/public.
npm run preview
First run the build command, and then start an HTTP server serving the dist/public folder.
HTML
Each index.html.jsx file in the src folder will output a corresponding index.html file in the dist/public folder.
Create a new HTML page
To render this web-page at /foo, then either create the file src/foo.html.jsx or the file src/foo/index.html.jsx. In this file, define a JSX component as the default export.
Note that .html.jsx files must render the whole HTML structure: <html>, <head>, and <body>. Only the <!doctype html> tag is left out.
[!IMPORTANT] Known limitation: If
config.build.emptyOutDiris turned on, then there can't be more than one.html.jsxfile in the same folder, due to the way the output files are overwritten indist/server.
The htmls prop
This prop carries a list of every .html.jsx entrypoint scattered in the src folder. By default, each object in htmls has the shape:
{
path: '/users/my-user/path/to/website.com/src/my-page/index.html.jsx',
link: '/my-page'
}If a .html.jsx file defines the __IMPORT_HTML_MODULES export as below, then its htmls prop will be incremented with the ESM modules of each .html.jsx file in src:
export const __IMPORT_HTML_MODULES = true
export default function Page({ htmls }) {
return (
<html>
<body>
<h2>List of Pages</h2>
{htmls.map => (
html => (
<a key={html.link} href={html.link}>
{html.module.title}
{/* Assuming each `.html.jsx` module exports a `title` */}
</a>
)
)}
</body>
</html>
)
}CSS
The cssLinks prop (global CSS imports)
In Vite, it's usual to straight up import CSS files:
import "./style.css"Each CSS file imported like this will automatically be appended to the cssLinks prop, which is injected into every .html.jsx entrypoint.
[!IMPORTANT] Known bug: during development, files imported in this way will be injected into all entrypoints, even if that entrypoint doesn't transitively depend on that CSS file. This bug doesn't happen in the production build.
[!TIP] For now, this can be mitigated by only including "global" CSS for globally shared components. For styles specific to a web-page, include them with the
?urlparameter. See the session below.
The cssLinks prop must be rendered as <link> tags, otherwise no CSS will be loaded by the final HTML page:
<head>
{cssLinks.map(cssLink => (
<link key={cssLink} rel="stylesheet" href={cssLink} />
))}
</head>Use the ?url parameter when importing:
import exclusiveStyleCSS from "./exclusive-style.css?url"
export default function SomePage({ cssLinks }) {
// assert exclusiveStyleCSS not in cssLinks
return (
<html>
<head>
<link rel="stylesheet" href={exclusiveStyleCSS} />
</head>
</html>
)
}In doing this, exclusive-style.css will not be present in the cssLinks prop, and must be manually rendered as a <link> tag.
Inlining
TO-DO: explore CSS inlining...
Vanilla JS
Global vanilla JS imports & the jsLinks prop
All JS files imported like below won't be evaluated at build-time, only at page-load-time; and their links will be appended to the jsLinks prop, which is injected into every .html.jsx entrypoint.
import "./my-script.js?vanilla"
export default function WebPage({ jsLinks }) {
return (
<html>
<body>
{jsLinks.map(jsLink => (
<script key={jsLink} src={jsLink} />
))}
</body>
</html>
)
}Like it's done for the cssLinks prop, the jsLinks prop must also be rendered as <script> tags.
Inlining
import "./my-script.js?vanilla&inline"my-script.js will be inlined into the final HTML as a base64 link.
Local vanilla JS imports
Doing import localJS from './local.js?url works fine for this use case. localJS won't be injected into jsLinks, and config.build.assetsInlineLimit will be followed properly.
Notes on further improvements
- Typescript
- Expand tests
- Will bringing back client-side rending during development speed up the hot-reloading?
- Somehow optimize asset copying during build
- Allow turning on
config.build.emptyOutDirwithout the limitation of at most one.html.jsxfile per folder - Respect
config.build.emptyOutDirbefore emptyingdist/public - Fix global CSS imports in development
- Follow
config.build.assetsInlineLimitfor auto-inlining vanilla JS files
