Build an Interactive Map with leaflet

· 5 min read · Updated March 17, 2026 · intermediate
leaflet interactive-maps data-viz r web-mapping

Leaflet is the most popular open-source JavaScript library for interactive maps. The R package leaflet brings this power to your R workflows, letting you create zoomable, pannable maps that work in RStudio, Shiny, and HTML documents.

In this project, you will build a complete map application that displays location data with custom markers, popups, and layer controls.

What You Will Build

By the end of this guide, you will have an interactive map that:

  • Displays markers for multiple locations
  • Shows custom popups with information
  • Includes layer controls to toggle different data types
  • Uses professional tile providers
  • Handles geographic data from CSV files

This pattern applies to any location-based data visualization you need to create.

Project Setup

First, create a new R script and install the necessary packages:

# Install required packages
install.packages(c("leaflet", "tidyverse", "readr"))

# Load libraries
library(leaflet)
library(tidyverse)
library(readr)

The leaflet package handles the mapping, while tidyverse and readr help you work with location data.

Preparing Your Data

For this project, you will create a simple dataset of locations. In real projects, this data might come from a CSV file, database, or API:

# Create a tibble with location data
locations <- tibble(
  name = c("Times Square", "Central Park", "Statue of Liberty", 
           "Empire State Building", "Brooklyn Bridge"),
  lat = c(40.7580, 40.7829, 40.6892, 40.7484, 40.7061),
  lng = c(-73.9855, -73.9654, -74.0445, -73.9857, -73.9969),
  type = c("attraction", "park", "landmark", "building", "landmark"),
  description = c("The bright lights of NYC", 
                 "Urban oasis in Manhattan",
                 "Symbol of freedom",
                 "Iconic skyscraper",
                 "Historic bridge")
)

locations

The tibble contains latitude, longitude, and descriptive information for each location.

Creating Your First Map

Start with the leaflet() function to initialize a map, then add tiles:

# Initialize the map
map <- leaflet() %>%
  addTiles()  # Add OpenStreetMap tiles by default

map

This gives you a basic interactive map. Now add your location data:

# Add markers for each location
map <- leaflet(data = locations) %>%
  addTiles() %>%
  addMarkers(~lng, ~lat, popup = ~name)

map

The ~ notation refers to columns in your data frame. Each marker now has a popup showing the location name.

Customizing Markers

Default markers work, but you can customize them for a professional look:

# Create a custom map with custom markers
map <- leaflet(data = locations) %>%
  addTiles() %>%
  addCircleMarkers(
    ~lng, ~lat,
    radius = 8,
    color = "red",
    fillColor = "orange",
    fillOpacity = 0.7,
    popup = paste0(
      "<b>", locations$name, "</b><br>",
      locations$description
    )
  )

map

Circle markers work better for dense data and give a cleaner look.

Adding Layer Controls

For more complex maps, add layer controls to let users toggle different types of locations:

# Create separate groups for each location type
map <- leaflet(data = locations) %>%
  addTiles() %>%
  
  # Add markers by type
  addCircleMarkers(
    data = filter(locations, type == "attraction"),
    ~lng, ~lat,
    group = "Attractions",
    radius = 8, color = "blue",
    popup = ~name
  ) %>%
  
  addCircleMarkers(
    data = filter(locations, type == "park"),
    ~lng, ~lat,
    group = "Parks",
    radius = 8, color = "green",
    popup = ~name
  ) %>%
  
  addCircleMarkers(
    data = filter(locations, type == "landmark"),
    ~lng, ~lat,
    group = "Landmarks",
    radius = 8, color = "red",
    popup = ~name
  ) %>%
  
  addCircleMarkers(
    data = filter(locations, type == "building"),
    ~lng, ~lat,
    group = "Buildings",
    radius = 8, color = "purple",
    popup = ~name
  ) %>%
  
  # Add layer control
  addLayersControl(
    overlayGroups = c("Attractions", "Parks", "Landmarks", "Buildings"),
    options = layersControlOptions(collapsed = FALSE)
  )

map

Now users can toggle each category on and off.

Using Custom Tile Providers

OpenStreetMap is great, but there are many other tile providers for different styles:

# Use CartoDB positron tiles (clean, light theme)
map <- leaflet(data = locations) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    ~lng, ~lat,
    radius = 8,
    color = "steelblue",
    fillColor = "lightblue",
    fillOpacity = 0.8,
    popup = paste0(
      "<b>", locations$name, "</b><br>",
      "<i>", locations$type, "</i><br>",
      locations$description
    )
  ) %>%
  addLegend(
    position = "bottomright",
    colors = c("blue", "green", "red", "purple"),
    labels = c("Attraction", "Park", "Landmark", "Building"),
    title = "Location Types"
  )

map

CartoDB tiles work well for data visualization because they do not compete with your data.

Complete Map Function

Wrap everything in a reusable function for production use:

create_location_map <- function(data, tile_provider = "CartoDB.Positron") {
  
  # Define colors by type
  colors <- c(
    "attraction" = "blue",
    "park" = "green", 
    "landmark" = "red",
    "building" = "purple"
  )
  
  # Build the map
  map <- leaflet(data = data) %>%
    addProviderTiles(tile_provider) %>%
    addCircleMarkers(
      ~lng, ~lat,
      radius = 8,
      color = ~colors[type],
      fillColor = ~colors[type],
      fillOpacity = 0.7,
      popup = paste0(
        "<b>", data$name, "</b><br>",
        "<i>", data$type, "</i><br>",
        data$description
      ),
      group = ~type
    ) %>%
    addLayersControl(
      overlayGroups = unique(data$type),
      options = layersControlOptions(collapsed = FALSE)
    ) %>%
    addLegend(
      position = "bottomright",
      colors = unique(colors[unique(data$type)]),
      labels = unique(data$type),
      title = "Location Types"
    )
  
  return(map)
}

# Use the function
create_location_map(locations)

This function is flexible enough to work with any location data you load.

Saving Your Map

To save the map as an HTML file for sharing:

# Save to HTML
htmlwidgets::saveWidget(
  create_location_map(locations),
  file = "nyc-locations-map.html",
  selfcontained = TRUE
)

This creates a standalone HTML file you can share with anyone.

Summary

You have built a complete interactive map application with:

  • Location data from a data frame
  • Custom circle markers with popups
  • Layer controls for filtering by type
  • Professional CartoDB tiles
  • A reusable function for production use
  • HTML export capability

The leaflet package makes it straightforward to create professional maps. The skills transfer directly to Shiny applications, R Markdown documents, and standalone HTML files.

See Also