# phoenix-liveview-realtime-cursor-tracking-tutorial **Repository Path**: mirrors_dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial ## Basic Information - **Project Name**: phoenix-liveview-realtime-cursor-tracking-tutorial - **Description**: โ†–๏ธ A tutorial from first principals for displaying multiple cursors from various people on a canvas in real time. - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-10-24 - **Last Updated**: 2026-01-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
# `Phoenix LiveView` Realtime Cursor Tracking Tutorial ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial/ci.yml?label=build&style=flat-square&branch=main) [![codecov.io](https://img.shields.io/codecov/c/github/dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial/main.svg?style=flat-square)](https://codecov.io/github/dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial?branch=main) [![Hex.pm](https://img.shields.io/hexpm/v/phoenix?color=brightgreen&style=flat-square)](https://hex.pm/packages/phoenix) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial/issues) [![HitCount](https://hits.dwyl.com/dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial.svg?style=flat-square&show=unique)](https://hits.dwyl.com/dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial) Learn how to create a **live cursor tracking app** using **Phoenix LiveView**!

- [`Phoenix LiveView` Realtime Cursor Tracking Tutorial](#phoenix-liveview-realtime-cursor-tracking-tutorial) - [Why? ๐Ÿคท](#why-) - [What? ๐Ÿ’ญ](#what-) - [Who? ๐Ÿ‘ค](#who-) - [Prerequisites](#prerequisites) - [How? ๐Ÿ’ป](#how-) - [Step 0: Run the finished app locally](#step-0-run-the-finished-app-locally) - [Clone the repo](#clone-the-repo) - [Download the Dependencies](#download-the-dependencies) - [Run the App](#run-the-app) - [Build It!](#build-it) - [1. Create the App ๐Ÿ†•](#1-create-the-app-) - [1a. Setting up `Tailwind CSS`](#1a-setting-up-tailwind-css) - [Installing TailwindCSS](#installing-tailwindcss) - [Setup watcher, minification and loading Tailwind](#setup-watcher-minification-and-loading-tailwind) - [1b. Running the Phoenix app](#1b-running-the-phoenix-app) - [2. LiveView with cursors ๐Ÿ”บ](#2-liveview-with-cursors-) - [2a. Tracking cursor movement](#2a-tracking-cursor-movement) - [3. Adding users :adult:](#3-adding-users-adult) - [3a. Adding usernames](#3a-adding-usernames) - [3b. Tracking who is online](#3b-tracking-who-is-online) - [4. Customization ๐ŸŽจ](#4-customization-) - [4a. Different colors](#4a-different-colors) - [5. Adding authentication ๐Ÿ”](#5-adding-authentication-) - [5a. Add a login button](#5a-add-a-login-button) - [5b. Add `auth_plug`](#5b-add-auth_plug) - [5c. Showing username](#5c-showing-username) - [Credits ๐Ÿ“](#credits-)
# Why? ๐Ÿคท As we're building our [MVP](https://github.com/dwyl/mvp), we are always trying to leverage the _awesome_ real-time features of `Phoenix` and `LiveView`. Live cursor tracking will enable _us_ to create a team-friendly environment in which `people` can _see_ what the others are clicking on when collaborating in a prioritization (e.g. backlog grooming) exercise. # What? ๐Ÿ’ญ This tutorial creates a stylized simple landing page showing the cursor of everyone that is connected to it. It should take you **20 minutes** to follow from start to finish. # Who? ๐Ÿ‘ค This tutorial is aimed at `LiveView` beginners who want to understand the ins and outs, while sparkling a bit of [`TailwindCSS`](https://tailwindcss.com/) magic :sparkles:. If you are completely new to `Phoenix` and `LiveView`, we recommend you follow the **`LiveView` _Counter_ Tutorial**: [dwyl/phoenix-liveview-counter-tutorial](https://github.com/dwyl/phoenix-liveview-counter-tutorial) ### Prerequisites This tutorial requires you have `Elixir` and `Phoenix` installed. If you you don't, please see [how to install Elixir](https://github.com/dwyl/learn-elixir#installation) and [Phoenix](https://hexdocs.pm/phoenix/installation.html#phoenix). # How? ๐Ÿ’ป ## Step 0: Run the finished app locally To get a feel of what you are going to build and to be _make sure_ it's working in 1 minute, do the following steps: ### Clone the repo Run the following commands to clone the repo. ``` git clone https://github.com/dwyl/phoenix-liveview-realtime-cursor-tracking-tutorial.git cd phoenix-liveview-realtime-cursor-tracking-tutorial ``` ### Download the Dependencies Install the dependencies by running the command: ```sh mix setup ``` This will download dependencies and compile your files. It might take a few minutes! ### Run the App Start the Phoenix server by running the command: ```sh mix phx.server ``` Now you can visit [`localhost:4000`](http://localhost:4000) in your web browser. > ๐Ÿ’ก Open a _second_ browser window or tab > (**in incognito mode**, if you want to), > and you will see the a new cursor with a new name! You should expect to see something similar to the following: ![cursor-tracking-demo-GIF](https://user-images.githubusercontent.com/194400/197960714-a82e945c-2511-4ff3-bbd1-47ed751ac3ce.gif) # Build It! Now that you've seen the app working, it's time to _build_ it from scratch! ## 1. Create the App ๐Ÿ†• In your terminal run the following `mix` command to generate the new Phoenix app: ```sh mix phx.new live_cursors --no-ecto --no-mailer --no-dashboard --no-gettext ``` This command will create a new `Phoenix` app and setup the dependencies (including the `LiveView` dependencies) The flags: `--no-ecto --no-mailer --no-dashboard --no-gettext` just mean we don't want a database, email, dashboard or translation. These are not needed in a simple demo/experiment app. When you see the following prompt in your terminal, press **`Y` to install the dependencies**. ```sh Fetch and install dependencies? [Yn] ``` Type Y followed by the Enter key. That will download all the necessary dependencies. ### 1a. Setting up `Tailwind CSS` We are going to be using `Tailwind` to style our page. If this is the first time using `Tailwind`, see: [dwyl/**learn-tailwind**](https://github.com/dwyl/learn-tailwind) for a primer. The reason we are using TailwindCSS is to showcase you how to also integrate a *mature* styling library and get it running with Phoenix so you can create your own awesome and beautiful web pages :smile:. #### Installing TailwindCSS Let's first add the TailwindCSS dependency to our project, by opening the `mix.exs` file and adding the following line to the `deps` section: ```elixir {:tailwind, "~> 0.1.9", runtime: Mix.env() == :dev}, ``` Next, in the `config/config.exs` file, let's specify the Tailwind version we are going to be using. ```elixir config :tailwind, version: "3.2.0", default: [ args: ~w( --config=tailwind.config.js --input=css/app.css --output=../priv/static/assets/app.css ), cd: Path.expand("../assets", __DIR__) ] ``` Now, simply run the following command to install the dependency: ```sh mix deps.get ``` And then run the following Tailwind tasks. These will download the standalone Tailwind CLI and generate a `tailwind.config.js` file in the `./assets` directory. ```sh mix tailwind.install mix tailwind default ``` ### Setup watcher, minification and loading Tailwind Throughout development we want Tailwind CLI to track our files for changes. For this, add the following watcher in `config/dev.exs` file. ```elixir watchers: [ //... tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}, ] ``` After developing our application, we want tailwind to minify our CSS files in production. For this to happen, update the following line in the `mix.exs` file. ```elixir defp aliases do [ setup: ["deps.get"], "assets.deploy": ["esbuild default --minify", "tailwind default --minify", "phx.digest"] ] end ``` All we have to do now is make sure tailwind classes are loaded in our `assets/css/app.css`. The tasks you ran previously probably already took care of this, but make sure you have these added to your file. ```elixir @import "tailwindcss/base"; @import "tailwindcss/components"; @import "tailwindcss/utilities"; //@import "./phoenix.css"; -> deleted ``` Also make sure your `assets/js/app.js` was changed as well. Make sure the `import "../css/app.css";` line is commented or removed. After all of this, you should be done! You can start styling your webpages with some sweet CSS :tada:. ### 1b. Running the Phoenix app To make sure everything is working properly, run the following: ```sh mix phx.server ``` If you visit [`localhost:4000`](http://localhost:4000), you'll see the following in your browser. phoenix_with_tailwind > Don't worry if this looks ugly. It's because we now use TailwindCSS. We're going to make it pretty in the next few minutes! ## 2. LiveView with cursors ๐Ÿ”บ Let's start by adding a LiveView to our application. Let's switch the current default route to a new `live` route in the `lib/live_cursors_web/router.ex` file. ```elixir scope "/", LiveCursorsWeb do pipe_through :browser live "/", Cursors end ``` Next, we create a `live` folder in `lib/live_cursors_web/live`, and create a file called `cursors.ex` with the following code. We are placing assigning `x` and `y` to the socket, which will refer to the mouse position within the page. ```elixir defmodule LiveCursorsWeb.Cursors do use LiveCursorsWeb, :live_view def mount(_params, _session, socket) do {:ok, socket |> assign(:x, 50) |> assign(:y, 50) } end end ``` In the same folder, we create a `cursors.html.heex` file with the follow code: ``` ``` > The reason we have the HTML template on a different file instead of using the `render` function with the `~H` sigil is purely for dev experience purposes. With VS Code, we can get TailwindCSS hints and code completion which we wouldn't otherwise using a `render` function in the `cursors.ex` file. Now delete all the code in the `lib/live_cursors_web/templates/page/index.html.heex` and change the `lib/live_cursors_web/templates/layout/root.html.heex` file. We just delete the header and render the content on our `cursors.html.heex` file. ```elixir <%= live_title_tag assigns[:page_title] || "LiveCursors", suffix: " ยท Phoenix Framework" %> <%= @inner_content %> ``` You should now see a cursor at the middle of the page. simple_cursor_middle > You can change your `svg` object to anything you like. > This custom cursor was created using [Figma](https://www.figma.com/). ### 2a. Tracking cursor movement We are going to use Javascript to track the mouse movement on the client and send it over to the Phoenix server we are building. We understand that the idea of LiveView is to do as much work on the server as possible, but in this specific scenario, that is not possible. Phoenix offers [options for client/server interoperability](https://hexdocs.pm/phoenix_live_view/js-interop.html), including **client hooks**. We are using the `phx-hook` binding for this. We are going to send events from Javascript to the server through this binding. In this case, we are using the `mousemove` event to send the updated coordinates to the server. For this, add the following code in the `assets/js/app.js`. ```javascript let Hooks = {}; Hooks.TrackClientCursor = { mounted() { document.addEventListener('mousemove', (e) => { const mouse_x = (e.pageX / window.innerWidth) * 100; // in % const mouse_y = (e.pageY / window.innerHeight) * 100; // in % this.pushEvent('cursor-move', { mouse_x, mouse_y }); }); } }; let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}}) ``` In the previous code block, we track the mouse position within the page in percentage. This way, the mouse position is displayed correctly in any device. The `mousemove` event handler is added to the `Hook` object which, in turn, is passed through the `liveSocket`. After this, we need to *handle the event* in the `cursors.ex` file. Let's add the event handler with the following code: ```elixir def handle_event("cursor-move", %{"mouse_x" => x, "mouse_y" => y}, socket) do {:noreply, socket |> assign(:x, x) |> assign(:y, y) } end ``` In the `cursors.html.heex` file, let's change the first two lines with the following piece of code: ```