Logo

dev-resources.site

for different kinds of informations.

How to host Browser.application projects

Published at
10/24/2024
Categories
hosting
elm
webdev
Author
dwayne
Categories
3 categories in total
hosting
open
elm
open
webdev
open
Author
6 person written this
dwayne
open
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.

elm Article's
30 articles in total
Favicon
10 Months of Elm to Angular
Favicon
25 Must-Check Elm Resources for Developers: Tutorials, Tools, and Tips
Favicon
๐Ÿƒ 30 Elm Picks to Fuel Your Functional Code
Favicon
Stateless and stateful components. No! Reusable views in Elm.
Favicon
On why I prefer not to use elm-css
Favicon
GitHub Actions, Devbox, and Elm
Favicon
Run Elm and lunarvim in a devcontainer
Favicon
How to host Browser.application projects
Favicon
How I host Elm web applications with GitHub Pages
Favicon
Announcing my newsletter "Elm with Dwayne"
Favicon
Using ChatGPT o1 to write UI code with Elm
Favicon
Exploring Svelte and Elm: A Comparative Look at Unique Frontend Technologies
Favicon
Exploring Frontend Technologies: Elm vs. Svelte
Favicon
Using a continuation-passing interpreter to make an interactive read operation for the browser with Elm
Favicon
How I use Devbox in my Elm projects
Favicon
Yet Another Tour of an Open-Source Elm SPA
Favicon
Elm 2023, a year in review
Favicon
For lack of a better name, Iโ€™m calling it โ€œThe Module Patternโ€.
Favicon
Exploring the Elm Architecture for Web Applications
Favicon
How to use Elm extensible records
Favicon
Elm with flex and color
Favicon
How I use Nix in my Elm projects
Favicon
Record Type Alias Combinators: A Public Service Announcement
Favicon
Parsing AWS AppSync Responses, Elm GraphQL Libraries, and Only Doing Front-End
Favicon
On continuation-passing style and the factorial function
Favicon
Announcing elm-integer
Favicon
Stricter TypeScript >, <, and ===
Favicon
Final Fantasy Legend Level Editor Update
Favicon
Rebuilding Final Fantasy Legend in Elm
Favicon
Building mobile apps using Elm and Capacitor

Featured ones: