Hi there, in the Elm guide, there’s a section on URL parsing where Evan goes through some examples of nested routes.
-- /blog/42
-- /blog/123
-- /blog/mosaic
I’m wondering how to get my app (in development and also production) to load blog/42 if I start there initially (e.g. type in localhost:8000/blog/42 or www.mysite.com/blog/42 in the navigation bar).
What DOES work:
I can do an initial load on a non nested route like /blog. Note this requires the --pushstate option on elm-live, which is what I’m using for development.
I can also click and navigate to /blog/42 by initially loading the home page and clicking a link to it. I.e. it’s the when I start at localhost:8000/blog/42 or www.example.com/blog/42 where I run into problems.
Looking at my console, it appears my server (elm-live here) is looking for my static content (index.html and elm.js) inside the ./blog directory in my project. That directory doesn’t exist, and so I get an error.
Anyone have any suggestions or ways to get around this?
Please let me know if anything is not clear and I’d be happy to clarify more, thank you!
This is a classic problem when trying to serve SPAs. You want the server to route all paths to index.html and let the client side app do the actual routing… How you achieve this depends on what server you are using. In general you can google for “spa routing [your server here]”. Below are a few examples:
Production
If you’re using nginx to serve your site you could add this following configuration line (taken from this article) to your nginx.conf:
try_files $uri $uri/ /index.html?$args;
If you’re deploying your application to a service like Netlify, you’ll need to add a configuration (taken from their docs to your _redirects file that looks like:
/* /index.html 200
Development
You’ll want to make sure your development server is handling the same. Some local servers do this by default. Looking through the elm-live docs, it looks like the --pushstate option you found is designed to deal with exactly this situation. Good find!
Thanks, I haven’t actually tried nginx nested routes (/blog/42) yet, hopefully that works.
The issue with elm-live is while --pushstate sends
localhost:8000/blog to the main index.html as expected. It does NOT send:
localhost:8000/blog/42 to the main index.html. Instead it sends it to ./blog/index.html, which doesn’t exist and I get an error.
I’m not married to elm-live though. While live reloading is nice, I’d much rather have this work. Do you (or anyone) know of a development server that can handle nested routes deeper than one level? What do you use?
EDIT: Thinking about this now, I haven’t actually set up both the /blog AND /blog/42 routes at the same time. I just did /blog, verified it worked, then modified it to be blog then an int (e.g. /blog/42 ) which didn’t work. Maybe it would work if I included them both? That is, /blog/42 would reroute to just /blog, which would reroute to the main index.html?.
Will test it out, happy to hear other thoughts in the meantime.
Hmmm, I’m not super familiar with elm-live but I’m sure it can handle this situation, that’s the whole purpose of the flag. The docs mention this plays with a --start-page flag. I wonder if you have to set that to the root index? Something like --pushstate --start-page=/index.html perhaps?
On my own projects, I usually use Parcel. You can see how I use it my elm game jam project. Of particular interest, this commit does all the setup. Unfortunately the project is not an SPA so you can’t see your problem in action. However, I just checked a private app I have that’s an SPA and it has the same setup and doesn’t seem to do anything special. IIRC Parcel supports this out of the box.
elm-live src/Main.elm -u -d public -- --output=public/dist/app.js
The index.html file can access any assets within the public folder. But make sure to use “/style.css” and “/dist/app.js”, instead of “./style.css” or “style.css”.
Using relative paths for assets without the leading slash will cause sub routes to look for files in the wrong place!
Also make sure to call Elm.Main.init from either a script tag within index.html or by including another script in the public folder.
As for production, I use netlify and provide a netlify.toml to redirect all requests to public/index.html
Hope this helps!
Edit: Here’s a link with an example in case the explanation was confusing:
elm-live src/Main.elm -u -- --output=elm.js definitely works whatever the number of path segments in your URL if you have an ./index.html file.
You most likely forgot the slash before elm.js in <script src="/elm.js"></script> in your index.html.
If you use <script src="elm.js"></script> instead without the slash, it will try to load the javascript file relatively to the current directory, which is ./blog/ when loading localhost:8000/blog/42, therefore trying to load ./blog/elm.js. This is the expected behavior with any server.
Reminder, that naming your bundle elm.js will confuse windows in some circumstances. Be save and use something else, that doesn’t shadow an executable. (elm in this case)