#Compile specific routes into static html

11 messages · Page 1 of 1 (latest)

atomic radish
#

I'm building an app that has the following routes:

  • home
  • about
  • contact
  • login
  • dashboard

The dashboard is really the only "dynamic" part of the entire app and it should only be accessible after a user logs in.

Question: Is it possible to compile my app so that home, about, and login are static html pages and dashboard is the only page served as a "SPA"? I'm interested in doing this to optimize for SEO and page load speed.

stone terrace
#

add this to your app.config

nova cipher
uncut drift
#

Hi, I too am looking for a similar setup:

Is there a way to implement pre-rendering with a SPA fallback as described in the React Router docs — but with multiple statically rendered entry points and, importantly, no server-side rendering (SSR)? The goal is to support fully static deployment.

<Router root={...}>
  <Route path='/' pre-render />
  <Route path='/about' pre-render />
  <Route path='/course/:slug' />
</Router>

Outputs:

dist/
├─ assets/
│  ├─ index.css
│  ├─ index.js
│  ├─ about.js
│  └─ course.js
├─ index.html
├─ about.html
└─ spa-fallback.html
nova cipher
#

Have you tried the nitro pre-render config where you can include or exclude specific routes.

uncut drift
#

Thanks @nova cipher and @stone terrace.

I'm using SolidStart with Nitro as the underlying engine, and I'm explicitly defining certain routes to be pre-rendered. The build process generates both server-side and client-side bundles. However, I ignore the server-side output and only use the contents of .output/public. With some custom scripting and file rearrangement, I handle routing through the CDN to achieve the desired behaviour.

For SPA fallback, I've defined an empty /spa-fallback route. I then modify the generated index.html from this route to match the main SPA index.html, effectively acting as a catch-all fallback.

There are still a few bugs to resolve, particularly when porting a client-side rendered app to server-side rendering. One key issue is preventing the use of browser-specific APIs on pre-rendered routes.

Is isServer the correct approach to guard components so that they only render in the browser, similar to use client directive?

nova cipher
#

With isServer you'll run into hydration issues. import components with clientOnly instead:
https://docs.solidjs.com/solid-start/reference/client/client-only#clientonly (will trigger Suspense).
Or you can create a wrapper which renders components only after the app has mounted on the client:


function ClientOnly(props:ParentProps){
 const [isMounted,setIsMounted] = createSignal(false);
 onMount(() => setIsMounted(true));
 return <Show when={isMounted()} fallback={"...loading"}>{props.children}</Show>
}
narrow thunder
stone terrace
narrow thunder
#

Thank you. Yes. I'm doing just that. I'm now running into a different issue that seems to be a bug (in vinxi? IDK).

I'm getting a "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client".

My app.tsx looks like this:

export default function App() {
  return (
    <Suspense>
      <Router root={Layout}>
        <FileRoutes />
      </Router>
    </Suspense>
  );
}

I reduced my Layout (causing the error) to just this:

export default function Layout(props: RouteSectionProps) {
  const user = createAsync(() => getUser(), {deferStream: true}); // If I remove this like, the code runs
  const location = useLocation();

  return (
    <div>
      Hello world!
    </div>
  );
}

I need to get the user (from the session) so that I can then render a private or public homepage accordingly.