THIS IS EXPERIMENTAL SOFTWARE.

You know where not to use it.

Vleam

Incrementally incorporate Gleam in Vue projects

Vleam (Vue + gLEAM) is a set of tools allowing developers to incrementally incorporate the Gleam programming language into their Vue projects.

Similarly to how Typescript helped Javascript handle what considered a large application in 2016, Gleam can help with what’s considered large today. Many apps already exists that are not going to be rewritten. No reason why they shouldn’t benefit from useful innovations, especially when they can be introduced slowly and incrementally, and particularly if it means less Typescript.

Vleam consists of the following parts:

  1. A Vite plugin that:
  1. A set of bindings to Vue’s APIs.

  2. An LSP that proxies messages between Gleam’s LSP for functionality in SFCs. Plugins are availble for Neovim and VSCode.

Prerequisites

  1. A Vue 3 project using Vite
  2. Working installation of Gleam’s CLI

Setup

The following examples use pnpm but yarn or npm should work as well.

First, setup a Gleam project in your Vue project’s root by using gleam new or by manually creating a gleam.toml file. Make sure you set target = "javascript".

Then, install Vleam:

pnpm add -D vleam
gleam add vleam

And add it as the first plugin in vite.config.ts:

import { vitePluginVueVleam } from "vleam";

export default defineConfig({
  // ... rest of your config ...
  plugins: [
    // Vleam first
    vitePluginVueVleam(),
    // Then the rest
    vue(),
    // ... rest of your plugins ...
  ],
});

Add src/vleam_generated to .gitignore.

For Neovim support, install vleam.nvim.

For VSCode support, install the vleam plugin.

If you’d like to get type information for Gleam code imported in Typescript, install ts-gleam as well:

pnpm add -D ts-gleam

and make sure gleam.toml has Typescript declarations configured:

[javascript]
typescript_declarations = true

That’s it!

Usage

To use Gleam code in TS or JS files, simply import as usual:

// this imports the function `new_todo` from `models.gleam`
import { new_todo } from "../vleam_todo/models.gleam";

In Vue SFCs, you can use Gleam like so:

// this code is inside <script lang="gleam"> </script>
import gleam/option.{Some}
import vleam/vue.{type Component, Prop, define_component, setup, with_1_prop}

// THIS FUNCTION MUST EXIST
pub fn default_export() -> Component {
  define_component([], [], False)
  |> with_1_prop(#(Prop("initialCount", Some(0))))
  // Props are handed as Computed to stay reactive
  |> setup(fn(props: #(Computed(Int)), _) {
    let initial_count = props.0

    let counter =
      initial_count
      |> vue.computed_value
      |> vue.ref

    let increment = fn() -> Int {
      let current_count =
        counter
        |> vue.ref_value

      counter
      |> vue.ref_set(current_count)

      current_count
    }

    // returning an Error will cause `setup` to throw it
    Ok(#(
      #("counter", counter),
      #("increment", increment),
    ))
  })
}

Note that a Gleam script block inside a Vue SFC, without exception, MUST declare a public default_export function that returns a Component. The Vite plugin assumes it exists and the build will fail without it.

For more information on Vue bindings in Gleam, see the reference at Hexdocs:

https://hexdocs.pm/vleam

Tips

// Easy
@external(javascript, "/src/composables/useTodoInputEvent", "useTodoInputEvent")
fn use_todo_input_event(event: InputEvent) -> Result(Todo, TodoError)

// Pain
@external(javascript, "../../../../composables/useTodoInputEvent", "useTodoInputEvent")
fn use_todo_input_event(event: InputEvent) -> Result(Todo, TodoError)

Limitations

HMR

HMR will trigger a full refresh until gleam-lang/gleam#3178 is fixed.

toRefs, reactive support

toRefs and reactive are not easy to translate into Gleam. Research required.

Thanks & Acknowledgements

Search Document