@mate-academy/lovable-build-tools
v2.0.0
Published
Reusable pre-rendering (React SSR via renderToString) and WebP conversion for Lovable (Vite + React) projects
Readme
@mate-academy/lovable-build-tools
SSR pre-rendering and WebP conversion CLI for Lovable (Vite + React) landing pages.
Part of the Generated Landings Infrastructure.
What it does
- SSR pre-render — builds an SSR bundle of your app with Vite, calls
ReactDOMServer.renderToStringfor each route, and writes a staticindex.htmlper route. React effects (useEffect,useLayoutEffect) don't run during SSR, so the HTML matches React's initial client render exactly — no hydration mismatches. - WebP conversion — converts raster assets in
dist/to WebP via Sharp and rewrites references in HTML and JS bundles.
Usage
1. Split App.tsx and add the SSR entry
Refactor src/App.tsx so the providers tree and the routes tree are separable,
then create src/entry-server.tsx that calls renderToString against
<StaticRouter>. The consumer src/main.tsx must also switch to hydrateRoot
when pre-rendered HTML is present.
See docs/LovableProjectSetupGuide.md (sections 1.3–1.5) for the canonical
code.
2. Declare routes
prerender.config.mjs:
export default {
routes: ["/", "/apply", "/privacy-policy"],
// Optional:
// generate404: true, // default true — writes dist/404.html
// entryServerPath: "src/entry-server.tsx", // default
};3. Wire up the build script
package.json:
{
"scripts": {
"build:prerender": "vite build && mate-prerender"
},
"devDependencies": {
"@mate-academy/lovable-build-tools": "^2.0.0"
}
}Pre-requisites in the consumer project
- All
<img>tags need explicit sizing (e.g.width/heightattrs, fixed dimensions, or Tailwindaspect-[W/H]). SSR cannot measure natural dimensions; CLS relies on CSS. - Components must be SSR-safe: no
window/document/localStorageat render time — always wrapped inuseEffector guarded withtypeof window !== "undefined". - Above-the-fold components must not use
React.lazy+<Suspense>. SSR emits the fallback and the client re-renders the subtree (React error #419). Import eagerly instead.
Migration from 1.x
Breaking changes in 2.0.0:
prerender()now usesrenderToStringinstead of Puppeteer. The package no longer depends onpuppeteer(install size roughly halves).- The consumer project must provide
src/entry-server.tsx(see above). viteis now a peer dependency — ensure the consumer has it installed (every Lovable project does).waitForSelectoroption is removed fromprerender.config.mjs(meaningless without a browser).- The CLI now runs
prerender()beforeconvertToWebP()(reversed from 1.x). The combined behavior is unchanged end-to-end.
Rollback: pin @mate-academy/[email protected] — that version keeps
the Puppeteer path. No dual path is maintained going forward.
