Skip to content

Svelte SPA Router – Invalid component object error

I’m using SvelteKit and svelte-spa-router.

My file structure looks like the following:

src/
├─ assets/
├─ lib/
│  ├─ Navbar.svelte
│  ├─ Sidebar.svelte
├─ routes/
│  ├─ about/
│  │  ├─ index.svelte
│  ├─ contact/
│  │  ├─ index.svelte
│  ├─ products/
│  │  ├─ index.svelte
│  ├─ Test.svelte
│  ├─ index.svelte
│  ├─ __layout.svelte
├─ app.css
├─ app.html

__layout.svelte:

(As per the example in the link above.)

<script>
  import Router from 'svelte-spa-router'
  import Navbar from "$lib/Navbar.svelte";
  import Sidebar from "$lib/Sidebar.svelte";
  import Home from './index.svelte'
  import About from './about/index.svelte'
  import Contact from './contact/index.svelte'
  import Products from './products/index.svelte'
  import Test from './Test.svelte';

  const routes = {
    // Exact path
    '/': Home,
    '/about': About,
    '/contact': Contact,
    '/products': Products,
    '/test': Test
  }
</script>

<Navbar />

<Sidebar />

<Router {routes} />

<style>
</style>

Error:

Error: Invalid component object
    at new RouteItem (Router.svelte:303:23)
    at Router.svelte:432:28
    at Array.forEach (<anonymous>)
    at Router.svelte:431:31
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (/src/routes/__layout.svelte:62:85)
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (eval at instantiateModule ([Path to project]/node_modules/vite/dist/node/chunks/dep-fcec4469.js:67775:28), <anonymous>:55:122)
    at $$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at Object.render ([Path to project]/node_modules/svelte/internal/index.js:1710:26)

Console:

Console Text

If I remove <Router {routes} /> and use <slot></slot>, everything works fine.

The only thing I managed to find about this error was this GitHub source code of Router.svelte (line #301), but it didn’t help much.

I tried changing the names of the components like in the example, but the error was still thrown (I figured maybe the fact that they’re all called the same might be a bug, I don’t know…).

Manually navigating via localhost:3000/[path] throws the error as well, including /test path which is outside in the same path as the __layout.svelte. The latter I mention because in the first link I provided the author said:

To display the router, in a Svelte component (usually App.svelte), first import the router component…

Usually from the examples I’ve seen, the structure you normally put in the App.svelte goes in the __layout.svelte and then the index.svelte, which serves as the “Home”/”Landing” page, automatically goes in the <slot /> (and whatever other routes you might have) located in __layout.svelte, by default.

Last but not least, dynamically importing them didn’t work either. See –> Edit 3

I understand a lot of the things I’ve tried probably have nothing to do with the problem, but the problem itself makes no sense to me. Like, why are the components getting passed as objects of a type the Router doesn’t understand? Test.svelte literally only has <h1>TEST COMPONENT</h1> in it.

Edit:

Added the following code to my __layout.svelte‘s <script></script> section:

// Contains logging information used by tests
let logbox = ''
// Handles the "conditionsFailed" event dispatched by the router when a component can't be loaded because one of its pre-condition failed
function conditionsFailed(event) {
  // eslint-disable-next-line no-console
  console.error('Caught event conditionsFailed', event.detail)
  logbox += 'conditionsFailed - ' + JSON.stringify(event.detail) + 'n'
  // Replace the route
  replace('/wild/conditions-failed')
}
// Handles the "routeLoaded" event dispatched by the router after a route has been successfully loaded
function routeLoaded(event) {
  // eslint-disable-next-line no-console
  console.info('Caught event routeLoaded', event.detail)
  logbox += 'routeLoaded - ' + JSON.stringify(event.detail) + 'n'
}
// Handles the "routeLoading" event dispatched by the router whie a route is being loaded
// If the route is dynamically imported, such as with the `import()` syntax, then there might be a delay before the route is loaded
function routeLoading(event) {
  // eslint-disable-next-line no-console
  console.info('Caught event routeLoading', event.detail)
  logbox += 'routeLoading - ' + JSON.stringify(event.detail) + 'n'
}
// Handles event bubbling up from nested routes
function routeEvent(event) {
  // eslint-disable-next-line no-console
  console.info('Caught event routeEvent', event.detail)
  logbox += 'routeEvent - ' + JSON.stringify(event.detail) + 'n'
}

After which I put the following below it, as shown in this test example:

<Router 
  {routes} 
  on:conditionsFailed={conditionsFailed}
  on:routeLoaded={routeLoaded}
  on:routeLoading={routeLoading}
  on:routeEvent={routeEvent}
/>

None of these got called, there was only the red console message seen in the picture above.

Edit 2:

As suggested by Thomas Hennes, I replaced <Router {routes} /> with each component individually in my __layout.svelte file, like so:

<script>
  import Router from 'svelte-spa-router'
  import Navbar from "$lib/Navbar.svelte";
  import Sidebar from "$lib/Sidebar.svelte";
  import Home from './index.svelte'
  import About from './about/index.svelte'
  import Contact from './contact/index.svelte'
  import Products from './products/index.svelte'
  import Test from './Test.svelte';

  const routes = {
    // Exact path
    '/': Home,
    //'/about': About,
    //'/contact': Contact,
    //'/products': Products,
    //'/test': Test
  }
</script>

<Navbar />

<Sidebar />

<Routes {routes} />

<style>
</style>

None of them worked.

Edit 3:

Funny thing, I noticed dynamically importing the routes crashes my local server. xD

const routes = {
  // Exact path
  '/': wrap({
        asyncComponent: () => import('./index.svelte')
  }),
  '/about': wrap({
        asyncComponent: () => import('./about/index.svelte')
  }),
  '/contact': wrap({
        asyncComponent: () => import('./contact/.svelte')
  }),
  '/products': wrap({
        asyncComponent: () => import('./products/.svelte')
  }),
  '/test': wrap({
        asyncComponent: () => import('./Test.svelte')
  }),
}

It doesn’t matter which one I import, if I only import one of them or all of them at the same time.

This is the console output of this adventure:

window is not defined
ReferenceError: window is not defined
    at getLocation (Router.svelte:38:31)
    at start (Router.svelte:59:23)
    at Object.subscribe ([Path to project]/node_modules/svelte/store/index.js:51:20)
    at Router.svelte:493:36
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (/src/routes/__layout.svelte:112:85)
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (eval at instantiateModule ([Path to project]/node_modules/vite/dist/node/chunks/dep-fcec4469.js:67775:28), <anonymous>:55:122)
    at $$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at Object.render ([Path to project]/node_modules/svelte/internal/index.js:1710:26)
history is not defined
ReferenceError: history is not defined
    at Router.svelte:455:10
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (/src/routes/__layout.svelte:112:85)
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (eval at instantiateModule ([Path to project]/node_modules/vite/dist/node/chunks/dep-fcec4469.js:67775:28), <anonymous>:55:122)
    at $$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at Object.render ([Path to project]/node_modules/svelte/internal/index.js:1710:26)
    at render_response (file://[Path to project]/node_modules/@sveltejs/kit/dist/ssr.js:561:28)
    at async respond_with_error (file://[Path to project]/node_modules/@sveltejs/kit/dist/ssr.js:1148:10)
    at async respond$1 (file://[Path to project]/node_modules/@sveltejs/kit/dist/ssr.js:1392:4)
/node_modules/svelte-spa-router/Router.svelte:413
            const match = routesList[i].match(newLoc.location);
                                                     ^

TypeError: Cannot read properties of null (reading 'location')
    at eval (/node_modules/svelte-spa-router/Router.svelte:413:45)
    at Object.subscribe ([Path to project]/node_modules/svelte/store/index.js:53:9)
    at eval (/node_modules/svelte-spa-router/Router.svelte:406:29)
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (/src/routes/__layout.svelte:112:85)
    at Object.$$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at eval (eval at instantiateModule ([Path to project]/node_modules/vite/dist/node/chunks/dep-fcec4469.js:67775:28), <anonymous>:55:122)
    at $$render ([Path to project]/node_modules/svelte/internal/index.js:1702:22)
    at Object.render ([Path to project]/node_modules/svelte/internal/index.js:1710:26)
    at render_response (file://[Path to project]/node_modules/@sveltejs/kit/dist/ssr.js:561:28)

Answer

svelte-spa-router is designed as a routing solution for Svelte projects (which are client-side only). It is not meant to function in server-side rendered SvelteKit projects.

SvelteKit, which is a universal (CSR + SSR) framework, provides its own routing solution, which will work client-side and server-side.