Lesson 2 — Vera, how the gem works
The dispatch deck chassis ships with the Vera gem already installed and configured — it’s in the Gemfile, its Stimulus controllers are registered, its CSS is linked, and its JavaScript modules are pinned in the importmap. You don’t need to add anything to start using it.
What you do need is a sense of what’s actually there. The gem is the bridge between Rails-and-Phlex on one side and MapLibre-and- JavaScript on the other. Understanding what each part does, and where the seams between them are, will make the rest of this tutorial significantly easier to follow. This lesson walks through Vera’s contribution to the chassis — what it provides, what it doesn’t, and what its API surface looks like.
No code yet. We’ll start writing components in Lesson 3.
What MapLibre is
Before we look at Vera, a brief tour of what’s underneath it.
MapLibre GL JS is an open-source web map library. It started in 2020 as a community fork of Mapbox GL JS — when Mapbox went proprietary at version 2, the open-source community forked the last MIT-licensed version and continued developing it as MapLibre. Today it’s a mature, actively maintained library used by serious mapping teams.
Three things matter about MapLibre for our purposes.
It’s vector-tile-native. Older libraries like Leaflet were designed around raster tiles — pre-rendered PNG images of map imagery, which the browser stitched together into a viewport. Vector tiles are different. The server sends compact binary descriptions of features (roads, water, places), and the browser renders them with WebGL. The result is sharper at all zoom levels, supports runtime styling (recolour the map without asking the server), and is dramatically faster for dense data.
It’s declaratively styled. A MapLibre map is configured by a JSON style document — sources, layers, paint properties, and filter expressions. You don’t write imperative code that adds and removes elements. You declare a configuration, and MapLibre renders accordingly. This shape fits beautifully with how Phlex components work.
It’s WebGL-based. Performance is closer to a video game than to a typical web library. Half a million points on the screen at once is unremarkable; Leaflet starts struggling at a few thousand. This matters for Module 7 when we scale up the dataset.
The downside is that MapLibre’s API is large. The configuration JSON has dozens of layer types, hundreds of paint and layout properties, and a substantial expressions language for runtime styling. Writing it directly from a Stimulus controller is verbose, error-prone, and scatters configuration across files.
That’s where Vera comes in.
What Vera is
Vera wraps MapLibre with a Phlex DSL. The same JSON configuration you’d write by hand is generated from Phlex component code that reads the way Rails developers expect their code to read.
A typical piece of MapLibre configuration looks something like this:
|
|
The same thing in Vera’s DSL is shaped like:
|
|
You can clearly see the correspondance between the raw json and the ruby equivalent here. The key thing is that instead of creating and managing many stimulus controller files, you’ll be working with pure ruby code in custom components. This makes composing new maps a much easier prospect.
You’ll see in the next lesson how you can literally create a map with a single line of ruby code - can’t get much easier than that!
What’s in the gem
Vera has four major contributions to a Rails application:
1. The Phlex map components. Vera::Map is the main one.
It accepts a configuration via a block-based DSL with helpers
for sources, layers, popups, controls, and the drawing toolkit.
There are specialised components like Vera::Map::Marker for
declaring markers inline, and the gem composes well with your
own Phlex components (e.g., a domain-specific JobMarker that
extends Vera::Map::Marker).
2. Stimulus controllers. Three controllers are shipped with
the gem and registered in the chassis. vera--map manages the
MapLibre instance for a map element — initialising it, applying
the configuration, handling events, broadcasting state changes.
vera--actions is a small controller that connects buttons and
links to common map actions (fly to a point, fit a bounding box,
open a popup) without you writing any custom Stimulus.
vera--draw activates Geoman drawing tools when the page calls
for them.
3. Action helpers. A method called action_attrs on
Vera::Map generates the data attributes needed to wire up
buttons, links, or any element to the vera--actions controller.
A typical use: a button on a list of locations that, when
clicked, flies the map to that location and drops a marker. You
write the data attributes via action_attrs; Stimulus does
the rest. We’ll see this pattern starting in Module 5.
4. JavaScript module pins. The gem ships ESM modules
(vera/install, vera/map_controller, etc.) that are pinned in
the importmap. This is what lets the chassis load Vera’s
JavaScript through Rails 8’s importmap system without a build
step. There’s also a one-line registration helper —
registerVeraControllers(application) — that makes wiring the
controllers into your Stimulus application trivial.
What Vera does NOT do
Worth being explicit about the boundaries. Vera is not a complete GIS library. It doesn’t:
-
Provide spatial database integration. That’s PostGIS’s job. The gem doesn’t know about your models, your geometry columns, or your queries. The Rails application is responsible for producing GeoJSON; the gem renders it on a map.
-
Generate map tiles. Tile servers are a separate concern. Most of the tutorial uses Carto’s positron-styled vector tiles for the base map — they’re free, fast, and well-styled for data overlays. Module 7 introduces serving custom vector tiles from PostGIS.
-
Wrap every MapLibre feature. The gem’s DSL covers the common cases — sources, layers, paint properties, popups, controls, drawing — but MapLibre has features the DSL doesn’t expose. When you need to reach those, the gem provides escape hatches: the
extra:option on layers and markers passes through arbitrary properties, and thevera--mapStimulus outlet exposes the underlying MapLibre map object for cases where raw access is genuinely required. We’ll see all of this in Module 5. -
Replace your understanding of MapLibre. If you go deep into custom paint expressions, layer effects, or tile sources, you end up reading MapLibre’s documentation. The gem hides complexity for the common cases; it doesn’t change what’s underneath.
How it’s wired into the chassis
The chassis you cloned has Vera fully integrated already. Three specific places to know about:
Gemfile has the gem itself:
|
|
(Once Vera is published to RubyGems, this becomes the simpler
gem "vera", "~> 0.1" form. For the moment it’s a Git
reference.)
config/importmap.rb has the JavaScript pins:
|
|
The first two pins fetch MapLibre and Geoman from esm.run (a
CDN that converts npm packages to ESM modules). The remaining
pins are Vera’s own modules, which the gem serves via
Rails’s asset pipeline. The reader doesn’t need to think about
this; it just works.
app/javascript/controllers/index.js registers Vera’s
Stimulus controllers:
|
|
The first three lines are the standard Rails 8 Stimulus loading
pattern. The last two register Vera’s controllers — vera--map,
vera--actions, vera--draw — alongside whatever controllers
you’ve put in app/javascript/controllers/.
app/views/layouts/application.html.erb loads the necessary
stylesheets:
|
|
The MapLibre stylesheet is required for any page that renders a map. The Geoman stylesheet is only needed for pages that use the drawing toolkit, but it’s small — a couple of kilobytes gzipped — and shipping it globally simplifies the layout.
That’s all the wiring. From the developer’s perspective, using
Vera in a page is just rendering Vera::Map from a Phlex
component or directly in an ERB view.
The DSL surface in one diagram
A Vera::Map is configured via a block. Inside the block, you
have access to a small set of DSL methods:
|
|
The methods you’ll see most are:
m.source— declares a data source for the mapm.layer— adds a styled layer rendering data from a sourcem.marker— adds a single marker at a coordinatem.popup— adds a free-floating popupm.draw— enables the drawing toolkitm.control— adds a built-in control (navigation, scale, attribution)
We’ll meet each of these in turn. Sources and layers come up in Module 4. Popups and markers in Module 5. Drawing in Module 8. Controls in Lesson 5 of this module.
A note on the underscore-vs-kebab convention
You’ll notice the DSL uses snake_case for paint property names —
circle_radius, circle_color — even though MapLibre’s JSON uses
kebab-case (circle-radius, circle-color). The gem translates
between them.
This is one of those small details that matters because it
removes a class of subtle bugs. MapLibre is unforgiving about
property names: a typo doesn’t raise an error, the property is
just silently ignored. By accepting symbol keys with snake_case
(which is the Ruby convention), the gem catches typos at the
component level — circle_radious raises an error in a way that
circle-radious in JSON wouldn’t.
The same translation happens for layer types, source types, and other config keys. You write Ruby; the gem produces correct MapLibre JSON.
The two languages your application speaks
Stepping back, an application using Vera ends up speaking two languages.
On the server: Ruby, Rails, ActiveRecord, PostGIS. You write controllers that produce GeoJSON from spatial queries. You write Phlex components that declare maps with sources and layers. The thinking is database-shaped and component-shaped — exactly the rhythm Rails developers are used to.
In the browser: MapLibre renders the map and handles the interactions. The Stimulus controllers Vera ships translate the configuration the server produced into MapLibre calls, listen for events, and update state. You don’t write any of this JavaScript yourself for the common cases.
The boundary between the two is the data the controller emits — a JSON config the gem generates from the Phlex component, plus the data attributes for the action helpers. Crossing that boundary in either direction is rare in a well-designed application. Most chapters in this tutorial don’t cross it at all.
This is the architectural shape that makes spatial work feel like Rails work, instead of feeling like JavaScript work that happens to live in a Rails app.
Activity 1 — Inspect the importmap
In the dispatch deck app, open config/importmap.rb in your
editor. Notice the Vera pins (vera/install, vera/map_controller,
vera/actions_controller) and the third-party pins for MapLibre
and Geoman.
Now run bin/rails importmap:packages from the project root.
This prints the importmap as Rails will serve it to the
browser, expanded for each environment. You’ll see the entries
from config/importmap.rb plus any additional resolutions.
This is what the browser receives.
This is also a useful command to remember when troubleshooting import errors — if a module isn’t loading, checking the importmap output usually reveals whether the pin is configured correctly.
Activity 2 — See the registered Stimulus controllers
Start the application with bin/dev if it isn’t already running,
then open http://localhost:3000 in a browser. Sign in.
Open the browser’s developer tools (F12 in most browsers, Cmd+Option+I on macOS) and switch to the Console tab. Type:
|
|
You should see an array containing strings like "vera--map",
"vera--actions", "dropdown", "toast", and any other
controllers the chassis has registered. The double-dash before
map and actions is Stimulus convention for a namespaced
controller — they live in the vera/ namespace of registered
controllers.
The list of registered controllers is what determines whether a
data-controller="vera--map" attribute on an element does
anything. In Lesson 3 we’ll add a map element with that
attribute and watch the controller initialise it.
Where this leaves us
You now understand:
- What MapLibre is and what makes it different from Leaflet
- What Vera contributes — Phlex components, Stimulus controllers, action helpers, importmap pins
- What’s wired up in the chassis already (Gemfile, importmap, controllers, stylesheets)
- What the DSL looks like in shape
- The boundary between server and browser, and why most chapters don’t cross it
Lesson 3 is where we render a real map. We’ll add a /lab
route, a controller, and the first Phlex view that declares a
Vera::Map. Australia will appear on your screen.