Integrating vis.js with Phoenix Live View

3 years ago

One of the projects here at Drover required us to show a directed graph representation of a flow that happens inside that service and, after looking into various alternatives, we settled on

Since this project has a Phoenix LiveView back-office, I had to figure out how to integrate the graph drawing mechanism into the LV and the way we ended up doing that was by using the hooks mechanism to inject the data into vis.js and draw the graph after the view was mounted.

The Live View

defmodule OutstandingLittleServiceWeb.Diagrams.ShowView do
@moduledoc “””
Shows a particular diagram
use OutstandingLittleServiceWeb, :live_view
alias OutstandingLittleService.Data
alias OutstandingLittleService.Utils
  def render(assigns) do
<%= if @loading do %>
<div>Loading information…</div>
<% else %>
<h1><%= %></h1>
<h5><%= @diagram.uuid %></h5>
<h3><%= @diagram.description %></h3>
<div id=”diagram”
style=”width:800px; height:400px”
data-diagram-data=”<%= Poison.encode!(Utils.diagram_to_json(@diagram)) %>”></div>
<% end %>
  def mount(_params, _session, socket) do
{:ok, assign(socket, loading: true)}
  def handle_info({:load_diagram, params}, socket) do
diagram = Data.get_diagram!(params[“id”], eager_load: true)
loading: false,
diagram: diagram
  def handle_params(params, _uri, socket) do
send(self(), {:load_diagram, params})
{:noreply, assign(socket, loading: true)}

As you can see, it’s a pretty standard LV in which i add the phx-hook “Diagram” to the diagram div and also inject the JSON structure i want as a data attribute. Utils.diagram_to_json is merely a helper function that knows how to convert a diagram to a JSON representation that vis.js can use.

LiveView hooks

On app.js i then define the hook that will inject the data and initialise the graph:

import css from “../css/app.css”
import “phoenix_html”
import { Socket } from “phoenix”
import LiveSocket from “phoenix_live_view”
import diagramInit from “./diagram”
let csrfToken = document.querySelector(“meta[name=’csrf-token’]”)
let Hooks = {}
Hooks.Diagram = {
mounted() {
let data = this.el.getAttribute(“data-diagram-data”)
let liveSocket = new LiveSocket(
Socket, {
hooks: Hooks,
params: {
_csrf_token: csrfToken

Initialising the graph

Finally on diagram.js the graph gets initialised:

import { DataSet, Network } from ‘vis/index-network’;
function getOptions() {
// Return vis.js options here, like layout, physics, etc
// Ommited for brevity
function addNode(nodes, id, label, description, result = null) {
id: id,
label: “<b>” + label + “</b>”,
description: description
return nodes
function addLink(links, id, start_id, end_id, label = null) {
id: id,
from: start_id,
to: end_id,
arrows: “to”,
physics: “false”,
label: “<b>” + label + “</b>”
return links
function diagramInit(data) {
let diagram = data
  // Setup data
let nodes = []
diagram.components.forEach(component => {
nodes = addNode(nodes,,, component.description, component.result)
  // Setup links
let links = []
diagram.links.forEach(link => {
links = addLink(links,, link.start_component_id, link.end_component_id)
  // Setup graph
let container = document.getElementById(‘diagram’)
let data = { nodes: nodes, edges: links }
new Network(container, data, getOptions())
export default diagramInit


  • Vis is added to the project using “npm install vis”, which will add the entry to the package.json file for you.
  • Some code on the diagram.js file is obviously specific to my case, namely the links between the diagram components, which are used to tell vis how to connect things, but it boils down to adding the nodes each with a different id, and then telling vis the start and end id for each connection.
  • As always, your mileage may vary.

Final thoughts

Hopeful by now you should have a good idea how to integrate external JS libraries and make them work with LiveView. It’s basically just figuring out how to plug them into the JS hooks mechanism and pass the adequate data between them.

There are other ways to pass the data, data attributes was just the way that seemed the easiest. But you could probably also use events between JS and the LV to accomplish the same goal, or other strategies.

Happy Elixir'ing!

Integrating vis.js with Phoenix Live View was originally published in Drover Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.