Search code examples
javascriptreactjssolid-js

SolidJS rendering twice when using routing


I'm new to SolidJS and I've noticed that my app is rendering twice. I suspect this is due to the way I handle routing. I've created a simple example to illustrate the issue:

app.tsx

import { Suspense } from "solid-js";
import { useAssets } from "solid-js/web";
import { css, renderSheets, type StyleData } from "solid-styled";
import { Router, Route } from "@solidjs/router";
import Home from './routes/index';
import NotFound from "~/routes/[...404]";

function GlobalStyles() {
  css`
    @global {
      * {
        transition: all .2s linear;
      }
      body {
        margin: 0;
        font-family: 'Montserrat', -apple-system, -system-ui, BlinkMacSystemFont,
          'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
          'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
        font-size: 16px;
        color: #292929FF;
      }
      a {
        margin-right: 1rem;
      }
      main {
        margin: 0 auto;
        width: 100%;
        height: 100vh;
        display: flex;
        flex-direction: row;
      }
      .content-wrapper {
        width: calc(100vw - 350px);
        height: 100%;
        display: flex;
        flex-direction: column;
      }
      input {
        border: none;
        outline: none;
        box-shadow: none;
        background-color: transparent;
      }
    }
  `;
  return null;
}

export default function App() {

  return (
    <Router>
          <Suspense>
              <Route path="/" component={Home} />
          </Suspense>
    </Router>
  );
}

entry-client.tsx

// @refresh reload
import { render } from "solid-js/web";
import App from "./app";

// @ts-ignore
render(() => <App />, document.getElementById("app"));

entry-server.tsx

// @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server";

export default createHandler(() => (
  <StartServer
    document={({ assets, children, scripts }) => (
      <html lang="en">
        <head>
          <meta charset="utf-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <link rel="icon" href="/favicon.ico" />
          {assets}
        </head>
        <body>
          <div id="app">{children}</div>
          {scripts}
        </body>
      </html>
    )}
  />
));

index.tsx

import { Title } from "@solidjs/meta";
import Sidebar from "~/components/Sidebar";
import Navbar from "~/components/Navbar";

export default function Home() {
  return (
      <main>
        TEST
      </main>
  );
}

This is shown in the webpage:

...
<div id="app"><!--!$e0-0-0-0-0-0-0--><main data-hk="0-0-0-0-0-0-0-0-2-0-0-0-0-0-0-0">TEST</main><!--!$/e0-0-0-0-0-0-0--><main>TEST</main></div>

As shown in the rendered output, the element appears twice. What could be causing this duplication and how can I resolve it?

Thanks in advance!


Solution

  • It is because you wrap the route into a Suspense.

    Suspense is executed twice by design. The first time is to collect the underlying resources so that it can be suspended until those resources resolve to a value. By resource, we mean the resource signal created via createResource function. Suspense is executed for the second time when all underlying resources resolve to a value, so that the actual data is rendered.

    That being said, there are few issues with your approach.

    A route should be rendered directly under a router. A resource should go inside the route component. Any component that is placed under a Suspense should guard against undefined data, because resource will be undefined initially. A resource may throw an error, if it is fetching a remote value, so you should guard against any possible errors using an ErrorBoundary.

    It may not be good to jump to SolidStart without getting the basics of SolidJs. SolidStart is build on top of SolidJs. A good grasp on SolidJS requires you to know the Solid's execution paradigm and the basics of its API, including context, resources and transitions.

    Read more about Resource and Suspense API: