fastify-vite icon indicating copy to clipboard operation
fastify-vite copied to clipboard

How to get SPA routing fallbacks?

Open panta82 opened this issue 8 months ago • 1 comments

How do I make the server fall back to the index.html for any non-covered route, in the SPA mode?

This is needed to use libraries like react-router.

I tried adding this:

app.get('/*', {
    logLevel: 'warn',
    handler: (_, reply) => {
      return reply.html();
    },
  });

This works fine in dev mode, but in production mode, it clashes with static path handlers.

Error: Method 'HEAD' already declared for route '/*' with constraints '{}'
              at Router._on (/home/panta/dev/authoredin/danaway/node_modules/fastify/node_modules/find-my-way/index.js:301:13)
              at Router.on (/home/panta/dev/authoredin/danaway/node_modules/fastify/node_modules/find-my-way/index.js:149:10)
              at Object.addNewRoute (/home/panta/dev/authoredin/danaway/node_modules/fastify/lib/route.js:336:16)
              at Object.route (/home/panta/dev/authoredin/danaway/node_modules/fastify/lib/route.js:247:19)
              at Object._route [as route] (/home/panta/dev/authoredin/danaway/node_modules/fastify/fastify.js:288:27)
              at fastifyStatic (/home/panta/dev/authoredin/danaway/node_modules/@fastify/static/index.js:115:15)
              at Plugin.exec (/home/panta/dev/authoredin/danaway/node_modules/fastify/node_modules/avvio/lib/plugin.js:125:28)
              at Boot._loadPlugin (/home/panta/dev/authoredin/danaway/node_modules/fastify/node_modules/avvio/boot.js:432:10)
              at processTicksAndRejections (node:internal/process/task_queues:82:21)

It seems examples don't cover this. Eg. take this example: https://github.com/FrancoRATOVOSON/fastify-vite-examples/tree/main/fastify-vite/react-vanilla-spa

Install react-router.

Modify the App.tsx file like this:

import './App.css'
import { createBrowserRouter, Link, RouterProvider } from 'react-router';

function A() {
  return (
    <div className='w-full flex justify-center gap-16 mb-10'>
      <h1>A</h1>
      <Link to={'/'}>Back</Link>
    </div>
  )
}

function B() {
  return (
    <div className='w-full flex justify-center gap-16 mb-10'>
      <h1>B</h1>
      <Link to={'/'}>Back</Link>
    </div>
  )
}

function Menu() {
  return <div>
    <p><Link to={'/a'}>A</Link></p>
    <p><Link to={'/b'}>B</Link></p>
  </div>;
}

const router = createBrowserRouter([
  {
    path: '/',
    element: <Menu />,
  },
    {
      path: '/a',
      element: <A />,
    },
    {
      path: '/b',
      element: <B />,
    },
  ]
);

function App() {
  return <RouterProvider router={router}/>
}

export default App

Build & run & open in browser. All the links between A and B will work. But if you go to /a, for example, and then reload, the page will crash.

panta82 avatar Apr 21 '25 15:04 panta82

If you are using the example you linked, could you please try and update @fastify/vite to the latest version? An issue with serving public files using a wildcard route was fixed since the version in that repo

zanmato avatar Apr 24 '25 15:04 zanmato