diff --git a/docs/maplibre/openfreemap.ipynb b/docs/maplibre/openfreemap.ipynb
new file mode 100644
index 0000000000..2f52f8f42e
--- /dev/null
+++ b/docs/maplibre/openfreemap.ipynb
@@ -0,0 +1,102 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/openfreemap.ipynb)\n",
+    "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/openfreemap.ipynb)\n",
+    "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n",
+    "\n",
+    "**Using OpenFreeMap Vector Tiles**\n",
+    "\n",
+    "[OpenFreeMap](https://github.com/hyperknot/openfreemap) lets you display custom maps on your website and apps for free. It provides several vector tilesets for free, including:\n",
+    "\n",
+    "- liberty: https://tiles.openfreemap.org/styles/liberty\n",
+    "- positron: https://tiles.openfreemap.org/styles/positron\n",
+    "- bright: https://tiles.openfreemap.org/styles/bright\n",
+    "\n",
+    "For more information, please visit https://openfreemap.org/quick_start.\n",
+    "\n",
+    "\n",
+    "Uncomment the following line to install [leafmap](https://leafmap.org) if needed."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# %pip install \"leafmap[maplibre]\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import leafmap.maplibregl as leafmap"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "m = leafmap.Map(style=\"liberty\")\n",
+    "m"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![image](https://github.com/user-attachments/assets/0a9c8cb8-2b7e-4ca5-ba7b-183b8b3f54a6)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "m = leafmap.Map(style=\"bright\")\n",
+    "m"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "m = leafmap.Map(style=\"positron\")\n",
+    "m"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/docs/maplibre/overview.md b/docs/maplibre/overview.md
index b546e171e1..1ea8ad7ac9 100644
--- a/docs/maplibre/overview.md
+++ b/docs/maplibre/overview.md
@@ -410,12 +410,16 @@ Visualize ocean bathymetry in 3D.
 
 [![](https://i.imgur.com/m6NwSWG.png)](https://leafmap.org/maplibre/ocean_bathymetry)
 
+## Use OpenFreeMap vector tiles
+
+Use free vector tiles from OpenFreeMap.
+
+[![](https://github.com/user-attachments/assets/0a9c8cb8-2b7e-4ca5-ba7b-183b8b3f54a6)](https://leafmap.org/maplibre/openfreemap)
+
 ## Visualze Overture data
 
 Visualize Overture Maps data.
 
-[![](https://i.imgur.com/m6NwSWG.png)](https://leafmap.org/maplibre/ocean_bathymetry)
-
 [![](https://github.com/user-attachments/assets/e07986eb-cc5a-4f25-b7e0-bed480a415d3)](https://leafmap.org/maplibre/overture)
 
 ## PMTiles source and protocol
diff --git a/leafmap/maplibregl.py b/leafmap/maplibregl.py
index af3a2dfd8d..a79f5223ed 100644
--- a/leafmap/maplibregl.py
+++ b/leafmap/maplibregl.py
@@ -60,12 +60,12 @@ def __init__(
                 counter-clockwise from north. Defaults to 0.
             style (str, optional): The style of the map. It can be a string or a URL.
                 If it is a string, it must be one of the following: "dark-matter",
-                "positron", "voyager", "positron-nolabels", "dark-matter-nolabels",
-                "voyager-nolabels", or "demotiles". If a MapTiler API key is set,
-                you can also use any of the MapTiler styles, such as aquarelle,
-                backdrop, basic, bright, dataviz, landscape, ocean, openstreetmap, outdoor,
-                satellite, streets, toner, topo, winter, etc. If it is a URL, it must point to
-                a MapLibre style JSON. Defaults to "dark-matter".
+                "carto-positron", "voyager", "positron-nolabels", "dark-matter-nolabels",
+                "voyager-nolabels", "demotiles", "liberty", "bright", or "positron".
+                If a MapTiler API key is set, you can also use any of the MapTiler styles,
+                such as aquarelle, backdrop, basic, bright, dataviz, landscape, ocean,
+                openstreetmap, outdoor, satellite, streets, toner, topo, winter, etc.
+                If it is a URL, it must point to a MapLibre style JSON. Defaults to "dark-matter".
             height (str, optional): The height of the map. Defaults to "600px".
             controls (dict, optional): The controls and their positions on the
                 map. Defaults to {"fullscreen": "top-right", "scale": "bottom-left"}.
@@ -78,12 +78,17 @@ def __init__(
         """
         carto_basemaps = [
             "dark-matter",
-            "positron",
+            "carto-positron",
             "voyager",
             "positron-nolabels",
             "dark-matter-nolabels",
             "voyager-nolabels",
         ]
+        openfreemap_basemaps = [
+            "liberty",
+            "bright",
+            "positron",
+        ]
         if isinstance(style, str):
 
             if style.startswith("https"):
@@ -102,7 +107,11 @@ def __init__(
                 )
 
             elif style.lower() in carto_basemaps:
+                if style.lower() == "carto-positron":
+                    style = "positron"
                 style = construct_carto_basemap_url(style.lower())
+            elif style.lower() in openfreemap_basemaps:
+                style = f"https://tiles.openfreemap.org/styles/{style.lower()}"
             elif style == "demotiles":
                 style = "https://demotiles.maplibre.org/style.json"
             elif "background-" in style:
diff --git a/mkdocs.yml b/mkdocs.yml
index 4a59c4fcb8..89e5d3c096 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -209,6 +209,7 @@ nav:
           - maplibre/multiple_geometries.ipynb
           - maplibre/navigation.ipynb
           - maplibre/ocean_bathymetry.ipynb
+          - maplibre/openfreemap.ipynb
           - maplibre/overture.ipynb
           - maplibre/pmtiles.ipynb
           - maplibre/restrict_bounds.ipynb
diff --git a/pyproject.toml b/pyproject.toml
index a236a6beea..3a373e6b3d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,7 +30,7 @@ authors = [{name = "Qiusheng Wu", email = "giswqs@gmail.com"}]
 [project.optional-dependencies]
 backends = ["bokeh", "keplergl", "maplibre", "pydeck", "plotly"]
 lidar = ["ipygany", "ipyvtklink", "laspy", "panel", "pyntcloud[LAS]", "pyvista"]
-raster = ["localtileserver>=0.10.0", "jupyter-server-proxy", "rio-cogeo", "rioxarray", "netcdf4", "pynhd", "py3dep"]
+raster = ["localtileserver>=0.10.0", "jupyter-server-proxy", "rio-cogeo", "rioxarray", "netcdf4", "pynhd", "py3dep", "d2spy"]
 sql = ["psycopg2", "sqlalchemy"]
 apps = ["streamlit-folium", "voila", "solara"]
 vector = ["geopandas", "osmnx", "pmtiles", "flask", "flask-cors", "lonboard", "mapclassify"]
diff --git a/requirements_dev.txt b/requirements_dev.txt
index 74b82badbf..ca6fe92278 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -3,6 +3,7 @@ black[jupyter]
 bokeh
 boto3
 cogeo-mosaic
+d2spy
 # earthaccess
 ffmpeg-python
 geopandas>=1.0.0