JavaScript Sucks. Here's How to Build with Vite and Gleam

TheWiggles1 pts0 comments

JavaScript sucks. Here’s how to build with Vite and Gleam - Hendassa100k WebsiteJavaScript sucks. Here’s how to build with Vite and Gleam<br>Posted on Jul 11, 2026

TL;DR<br>I am not attacking JavaScript . I only wrote highlights of my personal frustrations with JS and how I mitigated them. Most of the article is about Gleam and Vite and goes over the challenges I faced and the reasoning behind my decisions.<br>Here&rsquo;s the template itself. You can check the source code and you can begin building.<br>For years, I&rsquo;ve felt this pain: the tooling is great, but the language is fundamentally flawed. JavaScript was never designed properly. I mean never. I&rsquo;ve worried to even touch this language for this exact reason. Web developers spent a decade building increasingly elaborate band-aids to make it work. While TypeScript provides a safety net, it often feels like weak tape holding together a structure that wasn&rsquo;t built to be sane for developers. I wanted something better that doesn&rsquo;t change my values.<br>That&rsquo;s when I discovered Gleam. Gleam is a type-safe, functional programming language. While I didn&rsquo;t wrote a single program in functional programming language, I realized that&rsquo;s exactly what a scripting language should be. Gleam offers proper type safety and a functional approach that makes writing robust, safe, and concurrent code - the very things the web requires. Unlike TypeScript, it doesn&rsquo;t try to wrap shit in gold. It throws all of that out the window, but still leaves you a way to interact with it.<br>However, Gleam was initially built for Erlang. JavaScript support was a later addition, meaning the tools are still maturing. On the other hand, the JavaScript ecosystem for build tools is unparalleled.<br>But when I tried to find tools for this and then I stumbled across Vite. Vite is the ultimate glue. It handles live reloading, bundling, and asset management and all with a massive plugin ecosystem. Because Vite is plugin driven, it doesn&rsquo;t care what language you are writing in, as long as it can eventually turn it into something the browser understands.<br>By combining Gleam&rsquo;s type-safe logic with Vite&rsquo;s build pipeline, we can get the best of both worlds: The safety of functional programming with the speed of modern web tooling.<br>You propably saying &ldquo;Dude, just use WASM&rdquo;. WASM is powerful, yes, but it isn&rsquo;t good for web development at least for now. It currently faces significant issues, including limited DOM access and issues with startup times due how it is designed. Even if WASM were perfect, we still need a native scripting language to interact with the browser. JavaScript is like the &ldquo;C&rdquo; of the web: it is the standard, the most adopted, and the most compatible way to talk to the DOM. C and JavaScript are going to outlive the human race and it propably will never change.<br>Speaking of C this is why Gleam&rsquo;s approach is so smart. Gleam treats JavaScript as a FFI. It doesn&rsquo;t try to replace the browser&rsquo;s native language; instead, it provides a way to interact with it safely. Gleam forces you to write type-safe code, protecting you from the JavaScript traps like null and unpredictable exceptions or even dynamic typing for god&rsquo;s sake, while still letting you use JS when you need to.<br>Technical Deep Dive<br>Understanding Gleam FFI<br>To build a production-ready app, you need to understand how Gleam interacts with the JavaScript runtime.<br>One of the main goals of Gleam is being multilingual. Gleam uses FFI to call JavaScript functions for that. You define the external function in your Gleam code and provide the path to the JS implementation:<br>@external(javascript, "./assets.ffi.js", "get_asset")<br>pub fn load_asset(url: String) -> Promise(AssetResult)

And Gleam&rsquo;s strongest features is that it does not support JS types directly. You cannot simply &ldquo;import&rdquo; a dynamic JS object into Gleam and expect it to stay safe. Instead, Gleam forces you to define a type that represents the data.<br>For example, you can create a type:<br>// This type has no constructors, so it cannot be initialized directly.<br>// It acts as a "handle" for the JS value.<br>pub type DateTime

@external(javascript, "./js.ffi.mjs", "now")<br>pub fn now() -> DateTime

By doing this, Gleam ensures that the rest of your application treats object as a type-safe citizen, while the actual messy work happens behind the FFI boundary.<br>Since JavaScript doesn&rsquo;t have a native Result type (it uses exceptions) or Gleams types in general, Gleam provides a gleam.mjs module. This allows to map JS try/catch blocks into Gleam&rsquo;s Result types for example:<br>import { Result$Ok, Result$Error } from './gleam.mjs';

export function get_foo_asset(img) {<br>try {<br>let result = get_asset("foo.png");<br>return Result$Ok(result);<br>} catch (err) {<br>return Result$Error(AssetError$AssetNotFound);

Building our first app with Lustre<br>With that in mind, let&rsquo;s build our first...

gleam rsquo javascript type vite language

Related Articles