The HTML standards will enable the static hosting behavior you want, on Laravel or any other provider.
INDEX.HTML
First add a base
element like this. Set it to /
by default, or set it to a value like /spa/
when deployed:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'>
<base href='/spa/' />
<title>Demo App</title>
<link rel='stylesheet' href='bootstrap.min.css'>
<link rel='stylesheet' href='app.css'>
</head>
<body>
<div id='root' class='container'></div>
<script type='module' src='vendor.bundle.js'></script>
<script type='module' src='app.bundle.js'></script>
</body>
</html>
REACT
React has some nice support for path based routing, as selllami also pointed out above. When the React app starts, you can initialize React routes from the index.html runtime value:
const props = {
viewModel: new AppViewModel(),
};
const baseElement = document.querySelector('base') as HTMLElement;
const base = baseElement?.getAttribute('href') || '/';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render (
<ErrorBoundary>
<BrowserRouter basename={base}>
<App {...props} />
</BrowserRouter>
</ErrorBoundary>
);
You can then define routes like this, and your code needs no awareness that the full URLs are of the form /spa/products
:
<Routes>
<Route path='/products/:id' element={<ProductsView {...productsProps} />} />
<Route path='/*' element={<HomeView {...homeProps} />} />
</Routes>
You can then use other React navigation features, such as these, with paths beginning from the subfolder:
import {useNavigate, useLocation} from 'react-router-dom';
const navigate = useNavigate();
const currentPath = useLocation().pathname;
navigate("/products");
<Link to="/products">View Products</Link>
DEPLOYMENT
You just need to edit the index.html
as part of a deployment step. This can be automated by some script if required:
const oldData = fs.readFileSync('index.html', 'utf8');
var regex = new RegExp("href='/'", 'g');
const newData = oldData.replace(regex, "href='/spa/'");
fs.writeFileSync('index.html', newData, 'utf8');
My demo SPA uses the above techniques, and I deploy it by dropping static content into either AWS Cloudfront or Docker based environments. It would also work on Laravel.