In my quest for learning a new language I’ve picked Elm and Elixir, I think both are great languages to learn but for this article, I will be focusing on Elm. To be more specific, I will guide you through the process of building a simple Google Chrome extension using Elm.

Here is what you should expect to get out of reading this article.

Why should you care about Elm?

Basic Elm, syntax & setup

How to build a Google Chrome extension, using Elm

What is Elm?

Elm is a functional programming language that compiles to Javascript. If you’ve played with React and Redux, you’ll find Elm a lot more fun and productive.

Elm project setup

I’m going to describe the setup process on OSX with npm but the process should be the same on Linux. Let us go ahead and create a directory for our example project; we will call it elm_chrome_ext .

$ mkdir elm_chrome_ext $ cd elm_chrome_ext $ npm install -g elm

You don’t need a project directory to install Elm, but I’ve created one because we will need it in just a second. You usually install Elm as a global package (that’s what npm -g does) so you can generate new projects quickly.

Basic Elm syntax

I won’t go much into the language, and how to use it in this article; I think the Elm docs and the guide are doing a great job on that. But if you get stuck, feel free to ask for help in the comments.

Inside the project folder, let’s create a sub-folder for the Elm source files so that we have everything organized. We will name it elmsrc .

$ mkdir elmsrc $ touch elmsrc/Main.elm $ cd elmsrc

Now that we have a folder for storing everything Elm related, we can start adding the necessary dependencies. First, let’s add the Elm core package.

$ elm package install Some new packages are needed. Here is the upgrade plan. Install: elm-lang/core 4.0.1 Do you approve of this plan? (y/n) y Downloading elm-lang/core Packages configured successfully! $ elm package install elm-lang/html To install elm-lang/html I would like to add the following dependency to elm-package.json: "elm-lang/html": "1.0.0 <= v < 2.0.0" May I add that to elm-package.json for you? (y/n) y Some new packages are needed. Here is the upgrade plan. Install: elm-lang/html 1.0.0 elm-lang/virtual-dom 1.0.1 Do you approve of this plan? (y/n) y Downloading elm-lang/html Downloading elm-lang/virtual-dom Packages configured successfully!

What we’ve done here is we’ve installed the core Elm package and the html package so we can get started.

Now to compile our code, we will use elm make elmsrc/Main.elm --warn --output="elm.js" . This command tells Elm to compile the Main.elm file and put the generated Javascript code into a file called elm.js in the current folder. I like to use the --warn flag just because the compilation output is slightly more verbose and so it helps me with my learning process.

We need to add some code to the Main.elm file before we can compile it, otherwise we’ll get compilation errors and we surely don’t want that. I’ve added comments (the lines that begin with -- ) to describe what each function does.

-- elmsrc/Main.elm module Main exposing (..) -- This line imports functions that generate HTML import Html exposing (Html, button, div, text) -- Html.App is slightly more complicated but you can think of it as supervisor for our little Elm app import Html.App as Html -- This will be the rendered HTML (an empty div) view : a -> Html b view model = div [] [] -- The update function will be used to update the rendered HTML update : a -> b -> number update msg model = case msg of _ -> 0 -- This is what puts everything together main : Program Never main = Html.beginnerProgram { model = 0, view = view, update = update }

$ elm make Main.elm --warn --output="../elm.js" Success! Compiled 1 module. Successfully generated ../elm.js

What is a Google Chrome extension?

Have you ever seen those icons on the right of your address bar? The ones you click to do various things? Those are Chrome extensions.

They are like your personal admin dashboard for all the sites you visit since they can be activated almost anywhere. So if you ever find yourself needing more general functionality across many sites, a Chrome extension is what you need.

Basic Chrome extension setup

Creating a Chrome extension skeleton is easy. All you need is a manifest.json file and an HTML file. The manifest file should be in the root of your project, so in this case it’s elm_chrome_ext/manifest.json .

{ "manifest_version": 2, "name": "Example extension with Elm", "description": "This is just a minimalistic example of how to build a Chrome extension with Elm", "version": "1.0", "browser_action": { "default_popup": "elmext.html" }, "permissions": [ "activeTab" ] }

Here is a simple HTML file that will do for now.

<!-- elmext.html--> <!DOCTYPE html> <html> <head> <title>Chrome Extension</title> </head> <body> TEST </body> </html>

Now, to load the code we have just created, we need to visit chrome://extensions in Google Chrome and click the Load unpacked extension… button.

Security policy

One of the major pains I had with trying to build the Google Chrome extension and Elm was a violation of Chrome’s CSP (Content Security Policy). Just remember to come back to this section if you ever get a message (in Chrome’s Developer Tools console) that looks like the following.

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' blob: filesystem: chrome-extension-resource:". Either the 'unsafe-inline' keyword, a hash ('sha256-eSfQRhteSlQwLyblZBiFx4pdpvFSsfB22IDT2h2qtWM='), or a nonce ('nonce-...') is required to enable inline execution.

To get around this issue, you need to defer the initialization of the Elm application to after the DOM gets loaded. So create a file called elm-import.js and add this code inside it.

// elm-import.js document.addEventListener('DOMContentLoaded', function() { var div = document.getElementById('widget'); Elm.Main.embed(div); });

Then include load the elm-import.js file inside your HTML.

<!DOCTYPE html> <html> <head> <title>URL Builder</title> <link href="popup.css" rel="stylesheet" /> <script type="text/javascript" src="elm.js"></script> <script type="text/javascript" src="elm-import.js"></script> </head> <body> <div id="widget" class="ut-widget"></div> </body> </html>

Making use of Elm

We have our extension working and we have our Elm code that compiles but does nothing yet. So the extension would work without our Elm code. Let’s change that.

We will add a small interactive counter to our extenstion that will be powered by Elm.

But first, let’s also add a tiny bit of styling so can see the buttons a little better.

/* popup.css */ .ut-widget { width: 200px; font-size: 18px; }

The Elm code won’t change too much either. The notable changes will be in the view and update functions where we need to add the buttons so we can interactively change the value of the counter and actually change the value (with the update function).

module Main exposing (..) import Html exposing (Html, button, div, text) import Html.App as Html -- We need to handle click event import Html.Events exposing (onClick) -- Here we are defining two possible values type Msg = Increment | Decrement view : a -> Html Msg view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ] update : Msg -> number -> number update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 main : Program Never main = Html.beginnerProgram { model = 0, view = view, update = update }

Not that you might need to reload the extension by going to the extensions page and hitting reload.

Clicking any of the +/- buttons will increment and decrement the counter.

Conclusion

You know the basics of building Google Chrome extensions now so let your mind go wild and make something useful.

One last thing

If you know other people how would love to read this, please share it. Also make sure you leave a comment below with any questions you have.