DEV Community

Dwayne Crooks
Dwayne Crooks

Posted on • Updated on

How to host Browser.application projects

An Elm web application that uses Browser.application is typically doing so in order to support client-side routing. Client-side routing is what allows your single-page application to have several views which can be accessed via different URLs without doing a page refresh. Because of the nature of client-side routing and because of how a web browser works when you navigate to a new URL, we need to tell our web server how to handle certain requests so that our web application can respond to those requests instead.

Server-side routing

With server-side routing, the routing is handled by the web server. For e.g.

mkdir website
cd website

echo "Home page" > index.html

mkdir about
echo "About page" > about/index.html

mkdir login
echo "Login page" > login/index.html

nix-shell -p caddy
caddy file-server -l ":3000"
Enter fullscreen mode Exit fullscreen mode

Create a new website directory with three files, index.html, about/index.html, and login/index.html. Then, install the Caddy web server in an isolated shell and tell it to serve the current directory at http://localhost:3000.

URL Resource
http://localhost:3000/ index.html
http://localhost:3000/about about/index.html
http://localhost:3000/login login/index.html

If you go to one of the above URLs using your browser, the browser would send the request to Caddy and Caddy would respond by sending the corresponding file back to the browser. This is how a web server works, i.e. the path in the URL is used by the web server to find a resource (in this case a static HTML file) to send back to a browser. The browser then renders the resource for you to see.

Client-side routing

With client-side routing, the routing is handled in the browser using JavaScript. The web server redirects certain requests to a single HTML page, usually index.html, which contains the JavaScript that:

  1. Manages the user's history with the History API. And,
  2. Hijacks clicks on internal links in order to stop the browser from asking the web server for the resource.
// Detect when the history changes, for e.g. the Back button is pressed
window.addEventListener("popstate", () => {
    render(window.location.pathname)
})

// Hijack clicks on anchor tags
document.addEventListener("click", (event) => {
    if (
      // You clicked an anchor tag
      event.target.tagName === "A" &&
      // And, you're going to a page on this domain
      event.target.origin === window.location.origin
    ) {
      // Don't ask the web server for that resource
      event.preventDefault()

      // Update the browser's history
      window.history.pushState({}, "", event.target.href)

      render(event.target.pathname)
    }
})

// Figure out which template to render when the page is first loaded
render(window.location.pathname)
Enter fullscreen mode Exit fullscreen mode

Please take a look at the complete client-side routing example.

What happens when you go directly to the About page?

When you go to https://csr-example.netlify.app/about using your browser:

  1. The browser sends the request to the web server.
  2. The web server redirects the request to the index.html file and sends the index.html file back to the browser.
  3. The browser loads the index.html file and when the page has loaded it renders the template for the /about path.

What happens when you click on the Login link?

When you click on the Login link that's on the About page:

  1. The click is intercepted by the document's click handler.
  2. It checks if you clicked on an internal link.
  3. If you did, then:
    1. It stops the browser from requesting the resource from the web server.
    2. It manually updates the browser's history using the History API.
    3. And, it renders the template for the /login path.

Making client-side routing work for your Elm web application

Now that you understand how client-side routing works you'd be happy to know that Browser.application handles it all for you except for the web server configuration part.

Caddy

To configure Caddy you have to use the try_files directive. See here for an example.

Netlify

Netlify allows you to use a _redirects file in your website's directory. See here for an example.

GitHub

GitHub doesn't allow you to configure the web server. However, there are workarounds if you really want to use GitHub.

Other hosting providers

These are the other hosting providers I've used that also work.

Examples

Here are some examples of Elm web applications that use Browser.application.

Bonus: Under the hood of Browser.application

Let's dig into the code for elm/browser to see for ourselves what Browser.application really does.

  • Start here in Browser.elm.
  • Immediately we see that it's actually implemented in JavaScript, here.
  • On this line it sets up a handler for the popstate event so that it can manage the browser's history using the History API.
  • And here, it implements the logic for hijacking clicks on anchor tags.
    • But when does it actually use that function? That happens in your compiled JavaScript application. setup is called here. The function is saved here. And finally, it's used here, as expected, for the click handler.
  • Lastly, you can see here how init gets the initial URL so that you can show different things when the page is first loaded.

I hope you're less intimidated by Browser.application now that you see how similar it works to the client-side routing example.

Conclusion

Browser.application is used when you want to support client-side routing. However, it requires you to be able to configure your web server to redirect requests to your index.html file. Since GitHub Pages doesn't allow you to customize their web server, you're forced to switch to another hosting service like Netlify, Render, or Cloudflare Pages. Thankfully, they all make it easy to do the configuration that's needed.

Further reading

Subscribe to my newsletter

If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, Elm with Dwayne. To learn more about it, please read this announcement.

Top comments (0)