Skip to content
A scroll progress indicator
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.


Type Name Latest commit message Commit time
Failed to load latest commit information.


Add a scroll progress indicator and track the scrollTop position of your app or a specific element.

elm package install chrisbuttery/elm-scroll-progress


You can scope to a specific element (e.g. and article) or to the document.

Article | Document


  1. Import the module.
  2. Create a new parent Msg type referencing the ScrollProgress Msg.
  3. Subscribe to an incoming port, ensuring incoming data is passed back to ScrollProgress.Progress.
  4. Cater for the ProgressMsg in your update function.
  5. Map emitted messages from ScrollProgress.view to the type teh parent view expects (Msg).
module Main exposing (..)
import ScrollProgress exposing (..)


type Msg
    = ProgressMsg ScrollProgress.Msg


subscriptions : Model -> Sub Msg
subscriptions model =
    Ports.onScroll (ProgressMsg << ScrollProgress.Progress)


update : Msg -> AppModel -> ( AppModel, Cmd Msg )
update message model =
    case message of
        ProgressMsg subMsg ->
                ( updatedProgessModel, progressCmd ) =
                    ScrollProgress.update subMsg model.progressModel
                ( { model | progressModel = updatedProgessModel }, ProgressMsg progressCmd )


view : AppModel -> Html Msg
view model =
    Html.div []
        [ ProgressMsg (ScrollProgress.view model.progressModel)
        , Html.h1 [] [ text "Read this " ]
        , Html.div [] [ text "Some article..." ]

Define an incoming port in your Ports.elm so JavaScript can pass in scrolling attributes.

port module Ports exposing (..)
import ProgressBar exposing (ScrollAttributes)

port onScroll : (ScrollAttributes -> msg) -> Sub msg

In your JavaScript, add an eventListener to listen for the "scroll" event on the window.
You can choose to target the document or a specifc element's targetScrollHeight.

var app = Elm.Main.embed(root);

var target = document.querySelector('.some-article');
// or target the full page height with:
// target = document.documentElement;

window.addEventListener('scroll', function() {
    scrollTop: document.documentElement.scrollTop || document.body.scrollTop,
    targetScrollHeight: target.scrollHeight,
    clientHeight: document.documentElement.clientHeight

Note: For this to work in Firefox, we need to check for document.documentElement.scrollTop otherwise fallback to document.body.scrollTop.


All types for defining colors are Maybe String.
By default, the color of the progress scale is defined as Just #1684f6 however these String values can be whatever CSS colors you wish e.g: "#336699", "honeydew", "rgba(0,0,0,0.5)", etc.

You can choose to overide this color or include a linear gradient.

To override the color of the element, define a new model for the Child inside of the Parent.

module Main exposing (..)
import ScrollProgress exposing (..)

type alias AppModel =
    { progressModel : ScrollProgress.Model

newChildModel : ScrollProgress.Model
newChildModel =
    { progress = 0
    , color = Just "hotpink"
    , from = Nothing
    , to = Nothing

initialModel : AppModel
initialModel =
    { progressModel = newChildModel }

To replace a flat color for a linear gradient, populate the from and to properties.

newChildModel : ScrollProgress.Model
newChildModel =
    { progress = 0
    , color = Nothing
    , from = Just "lightseagreen"
    , to = Just "lightsalmon"

Building examples

Install Create Elm App and run elm-app build or elm-app start inside of examples/article & examples/body.

Thanks goes to Michael Troy for creating the examples.  ·  GitHub @chrisbuttery  ·  Twitter @buttahz  ·  elm-lang slack @butters

You can’t perform that action at this time.