Ctrl + k

or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/react-leaflet@5.0.x

docs

index.md
tile.json

tessl/npm-react-leaflet

tessl install tessl/npm-react-leaflet@5.0.3

React components for Leaflet maps

tile-layers.mddocs/reference/

Tile Layers

Tile layer components display raster map tiles from tile servers. They form the base layer of most maps, providing the background cartography.

Related Documentation

  • Map Container - Container for tile layers
  • Media Overlays - Overlaying images on maps
  • Controls - Switching between tile layers
  • Layer Groups - Grouping tile layers

Capabilities

TileLayer Component

Displays tiled map layers loaded from a URL template.

/**
 * Component for displaying raster tile layers
 * @param props - Tile layer properties
 */
const TileLayer: FunctionComponent<TileLayerProps>;

interface TileLayerProps extends TileLayerOptions, LayerProps {
  /** URL template for tiles (e.g., "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") */
  url: string;
}

Usage Example:

import { MapContainer, TileLayer } from "react-leaflet";

function Map() {
  return (
    <MapContainer center={[51.505, -0.09]} zoom={13}>
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
    </MapContainer>
  );
}

Common Tile Providers:

// OpenStreetMap
<TileLayer
  url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
  attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
/>

// Stamen Terrain
<TileLayer
  url="https://stamen-tiles-{s}.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg"
  attribution='Map tiles by <a href="http://stamen.com">Stamen Design</a>'
/>

// CartoDB Positron
<TileLayer
  url="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png"
  attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="https://carto.com/attributions">CARTO</a>'
/>

// Mapbox (requires API key)
<TileLayer
  url="https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}"
  attribution='&copy; <a href="https://www.mapbox.com/">Mapbox</a>'
  id="mapbox/streets-v11"
  accessToken="YOUR_MAPBOX_ACCESS_TOKEN"
/>

// OpenTopoMap
<TileLayer
  url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
  attribution='Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>, SRTM | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a>'
  maxZoom={17}
/>

WMSTileLayer Component

Displays tiles from WMS (Web Map Service) servers.

/**
 * Component for displaying WMS tile layers
 * @param props - WMS tile layer properties
 */
const WMSTileLayer: FunctionComponent<WMSTileLayerProps>;

interface WMSTileLayerProps extends WMSOptions, LayerProps {
  /** WMS service base URL */
  url: string;
  /** WMS request parameters */
  params?: WMSParams;
}

Usage Example:

import { MapContainer, WMSTileLayer } from "react-leaflet";

function Map() {
  return (
    <MapContainer center={[51.505, -0.09]} zoom={13}>
      <WMSTileLayer
        url="https://example.com/wms"
        params={{
          layers: "my_layer",
          format: "image/png",
          transparent: true,
        }}
        attribution="WMS Service"
      />
    </MapContainer>
  );
}

Advanced WMS Example:

<WMSTileLayer
  url="https://geo.weather.gc.ca/geomet"
  params={{
    layers: "RADAR_1KM_RRAI",
    format: "image/png",
    transparent: true,
    version: "1.3.0",
    time: "2024-01-01T12:00:00Z",
  }}
  opacity={0.7}
  zIndex={1000}
/>

Types

TileLayerOptions

// From Leaflet
interface TileLayerOptions extends GridLayerOptions {
  /** Minimum zoom level for tiles */
  minZoom?: number;
  /** Maximum zoom level for tiles */
  maxZoom?: number;
  /** Subdomains of the tile service (for load balancing) */
  subdomains?: string | string[];
  /** Error tile URL shown when tile fails to load */
  errorTileUrl?: string;
  /** Zoom offset for tile coordinates */
  zoomOffset?: number;
  /** If true, inverse Y axis numbering (TMS) */
  tms?: boolean;
  /** If true, zoom bounds check */
  zoomReverse?: boolean;
  /** Tile size multiplier for retina displays */
  detectRetina?: boolean;
  /** Custom headers for tile requests */
  headers?: Record<string, string>;
  /** Crossorigin attribute for tiles */
  crossOrigin?: boolean | string;
  /** Referrer policy for tile requests */
  referrerPolicy?: string;
}

interface GridLayerOptions extends LayerOptions {
  /** Width and height of tiles in pixels */
  tileSize?: number | Point;
  /** Opacity of the tiles */
  opacity?: number;
  /** Whether to update map while tiles load */
  updateWhenIdle?: boolean;
  /** Whether to update map while zooming */
  updateWhenZooming?: boolean;
  /** Interval to throttle updateWhenZooming */
  updateInterval?: number;
  /** z-index for the layer */
  zIndex?: number;
  /** Geographic bounds of the layer */
  bounds?: LatLngBoundsExpression;
  /** Keep this many rows/cols of tiles loaded */
  keepBuffer?: number;
  /** Class name for tile container */
  className?: string;
}

WMSOptions

// From Leaflet
interface WMSOptions extends TileLayerOptions {
  /** Comma-separated list of WMS layers */
  layers?: string;
  /** Comma-separated list of WMS styles */
  styles?: string;
  /** Image format (e.g., 'image/png') */
  format?: string;
  /** Whether to request transparent tiles */
  transparent?: boolean;
  /** WMS version string */
  version?: string;
  /** Coordinate Reference System */
  crs?: CRS;
  /** Whether to use uppercase parameter names */
  uppercase?: boolean;
}

interface WMSParams {
  /** WMS layers parameter */
  layers: string;
  /** WMS styles parameter */
  styles?: string;
  /** Image format */
  format?: string;
  /** Whether to request transparent tiles */
  transparent?: boolean;
  /** WMS version */
  version?: string;
  /** Additional WMS parameters */
  [key: string]: string | number | boolean;
}

LayerProps

interface LayerProps extends EventedProps, LayerOptions {
  /** Pane where the layer should be rendered */
  pane?: string;
  /** Attribution text for the layer */
  attribution?: string;
}

interface LayerOptions {
  pane?: string;
  attribution?: string;
}

interface EventedProps {
  /** Map of event types to handlers */
  eventHandlers?: LeafletEventHandlerFnMap;
}

Important Notes

  1. URL Template Variables: Tile URLs support these template variables:

    • {z} - Zoom level
    • {x} - Tile X coordinate
    • {y} - Tile Y coordinate
    • {s} - Subdomain (from subdomains option)
    • {r} - Retina marker (@2x)
  2. Attribution: Always include proper attribution for tile providers as required by their terms of service.

  3. Retina Tiles: Use detectRetina: true to automatically load higher resolution tiles on retina displays:

    <TileLayer
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      detectRetina={true}
    />
  4. CORS: Some tile servers require CORS configuration. Use the crossOrigin prop if needed:

    <TileLayer
      url="https://example.com/{z}/{x}/{y}.png"
      crossOrigin={true}
    />
  5. Multiple Tile Layers: You can stack multiple tile layers. Use opacity for blending:

    <TileLayer url="base-layer-url" />
    <TileLayer url="overlay-layer-url" opacity={0.5} />
  6. Z-Index Control: Use zIndex to control layer stacking order:

    <TileLayer url="background-url" zIndex={1} />
    <TileLayer url="foreground-url" zIndex={2} />
  7. WMS Parameters: WMS layers require specific parameters. Common ones include:

    params={{
      layers: "layer1,layer2",
      format: "image/png",
      transparent: true,
      version: "1.1.1",
    }}
  8. Tile Loading Events: Use event handlers to track tile loading:

    <TileLayer
      url="..."
      eventHandlers={{
        loading: () => console.log("Tiles loading"),
        load: () => console.log("Tiles loaded"),
        tileerror: (error) => console.error("Tile error", error),
      }}
    />
  9. Custom Panes: Render tiles in specific panes using the pane prop:

    <TileLayer url="..." pane="tilePane" />
  10. Bounds Restriction: Limit tile loading to specific geographic bounds:

    <TileLayer
      url="..."
      bounds={[[40.7, -74.0], [40.8, -73.9]]}
    />
  11. Tile Size: Customize tile size for non-standard tile servers:

    <TileLayer
      url="..."
      tileSize={512}
      zoomOffset={-1} // Adjust zoom offset when using different tile sizes
    />
  12. TMS Coordinate System: Some tile servers use TMS (Tile Map Service) coordinate system:

    <TileLayer
      url="..."
      tms={true} // Inverts Y axis numbering
    />

Examples

Switching Between Tile Layers

function MapWithLayerSwitch() {
  const [layer, setLayer] = useState("osm");

  return (
    <>
      <select value={layer} onChange={(e) => setLayer(e.target.value)}>
        <option value="osm">OpenStreetMap</option>
        <option value="topo">Topographic</option>
      </select>
      <MapContainer center={[51.505, -0.09]} zoom={13}>
        {layer === "osm" && (
          <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
        )}
        {layer === "topo" && (
          <TileLayer url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png" />
        )}
      </MapContainer>
    </>
  );
}

Tile Layer with Custom Subdomains

<TileLayer
  url="https://{s}.mytiles.com/{z}/{x}/{y}.png"
  subdomains={["a", "b", "c", "d"]}
  attribution="Custom tiles"
/>

WMS Layer with Multiple Layers

<WMSTileLayer
  url="https://wms.example.com/service"
  params={{
    layers: "layer1,layer2,layer3",
    format: "image/png",
    transparent: true,
    version: "1.3.0",
  }}
/>

Tile Layer with Loading Indicator

function TileLayerWithLoading() {
  const [loading, setLoading] = useState(true);

  return (
    <>
      {loading && <div className="loading-spinner">Loading tiles...</div>}
      <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        eventHandlers={{
          loading: () => setLoading(true),
          load: () => setLoading(false),
        }}
      />
    </>
  );
}

Overlay Tile Layer

function MapWithOverlay() {
  const [showOverlay, setShowOverlay] = useState(true);

  return (
    <MapContainer center={[51.505, -0.09]} zoom={13}>
      <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
      {showOverlay && (
        <TileLayer
          url="https://tiles.example.com/overlay/{z}/{x}/{y}.png"
          opacity={0.5}
          zIndex={1000}
        />
      )}
    </MapContainer>
  );
}

Troubleshooting

Tiles Not Loading

Problem: Tiles don't appear or show error icons.

Possible Causes and Solutions:

  1. Incorrect URL:

    • Verify the tile URL template is correct
    • Check that all template variables ({z}, {x}, {y}, {s}) are present
    • Test the URL in a browser by replacing variables with actual values
  2. CORS Issues:

    <TileLayer
      url="..."
      crossOrigin={true}
    />
  3. Attribution Missing:

    • Some tile providers require attribution to function
    • Always include proper attribution
  4. Rate Limiting:

    • Some tile servers have rate limits
    • Implement caching or use a different provider
    • Consider using your own tile server
  5. Zoom Level Out of Range:

    <TileLayer
      url="..."
      minZoom={0}
      maxZoom={18}
    />

Tiles Loading Slowly

Problem: Tiles take a long time to load.

Solutions:

  1. Use Subdomains for Parallel Loading:

    <TileLayer
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      subdomains={['a', 'b', 'c']}
    />
  2. Reduce Tile Buffer:

    <TileLayer
      url="..."
      keepBuffer={2} // Default is 2, reduce for less memory usage
    />
  3. Optimize Update Behavior:

    <TileLayer
      url="..."
      updateWhenIdle={true} // Only update tiles when map is idle
      updateWhenZooming={false} // Don't update during zoom
    />
  4. Use CDN or Faster Tile Server:

    • Consider using a CDN-backed tile provider
    • Host your own tile server closer to users

Retina Display Issues

Problem: Tiles look blurry on high-DPI displays.

Solution:

<TileLayer
  url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
  detectRetina={true}
/>

Or use explicit retina tiles:

<TileLayer
  url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}@2x.png"
/>

Tile Seams or Gaps

Problem: Visible gaps between tiles.

Solutions:

  1. Ensure Correct Tile Size:

    <TileLayer
      url="..."
      tileSize={256} // Standard tile size
    />
  2. Check Zoom Offset:

    <TileLayer
      url="..."
      tileSize={512}
      zoomOffset={-1} // Adjust for non-standard tile sizes
    />
  3. Disable Tile Fading:

    <MapContainer fadeAnimation={false}>
      <TileLayer url="..." />
    </MapContainer>

WMS Layer Not Displaying

Problem: WMS layer doesn't appear.

Solutions:

  1. Verify WMS Parameters:

    <WMSTileLayer
      url="https://example.com/wms"
      params={{
        layers: "layer_name", // Must match server layer name exactly
        format: "image/png",
        transparent: true,
        version: "1.3.0", // Check server version
      }}
    />
  2. Check CRS Compatibility:

    <WMSTileLayer
      url="..."
      params={{ ... }}
      crs={L.CRS.EPSG4326} // Match server CRS
    />
  3. Test WMS URL Directly:

    • Build a GetMap request manually and test in browser
    • Verify server is accessible and responding

Memory Issues with Many Tiles

Problem: Browser crashes or slows down with many tiles.

Solutions:

  1. Reduce Keep Buffer:

    <TileLayer
      url="..."
      keepBuffer={1} // Reduce from default of 2
    />
  2. Limit Zoom Levels:

    <TileLayer
      url="..."
      minZoom={5}
      maxZoom={15} // Prevent loading too many tiles at high zoom
    />
  3. Use Update When Idle:

    <TileLayer
      url="..."
      updateWhenIdle={true}
    />

Tile Attribution Not Showing

Problem: Attribution text doesn't appear.

Solutions:

  1. Enable Attribution Control:

    <MapContainer attributionControl={true}>
      <TileLayer
        url="..."
        attribution='&copy; <a href="...">Provider</a>'
      />
    </MapContainer>
  2. Check Attribution Control Position:

    <AttributionControl position="bottomright" />
  3. Verify Attribution String:

    • Ensure attribution prop is set on TileLayer
    • Use HTML entities correctly (© for ©)

Custom Tile Server Issues

Problem: Custom tile server tiles not loading.

Solutions:

  1. Verify Tile Naming Convention:

    • Standard: /{z}/{x}/{y}.png
    • TMS: /{z}/{x}/{y}.png with tms: true
    • Quadkey: Use custom tile layer implementation
  2. Check Server CORS Headers:

    • Server must send appropriate CORS headers
    • Add Access-Control-Allow-Origin: * header
  3. Test Tile URLs:

    // Log tile URLs for debugging
    <TileLayer
      url="..."
      eventHandlers={{
        tileloadstart: (e) => console.log('Loading tile:', e.url),
        tileerror: (e) => console.error('Tile error:', e.url),
      }}
    />

Performance Best Practices

  1. Use Subdomains: Enables parallel tile loading
  2. Implement Tile Caching: Cache tiles on server or CDN
  3. Optimize Tile Size: Use standard 256x256 or 512x512 tiles
  4. Limit Zoom Levels: Don't offer unnecessary zoom levels
  5. Use Vector Tiles: Consider vector tiles for better performance (requires different approach)
  6. Lazy Load Tiles: Use updateWhenIdle: true for better performance
  7. Monitor Network: Track tile loading performance and errors
  8. Use CDN: Serve tiles from CDN for faster loading
  9. Compress Tiles: Use WebP or optimized PNG format
  10. Implement Fallbacks: Provide error tiles for failed loads

Testing Tile Layers

Unit Testing TileLayer

import { render } from '@testing-library/react';
import { MapContainer, TileLayer } from 'react-leaflet';

test('renders tile layer', () => {
  const { container } = render(
    <MapContainer center={[51.505, -0.09]} zoom={13}>
      <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        attribution='&copy; OpenStreetMap'
      />
    </MapContainer>
  );
  
  expect(container).toBeInTheDocument();
});

Testing WMS Layer

test('renders WMS tile layer with params', () => {
  render(
    <MapContainer center={[51.505, -0.09]} zoom={13}>
      <WMSTileLayer
        url="https://example.com/wms"
        params={{
          layers: 'test_layer',
          format: 'image/png',
          transparent: true,
        }}
      />
    </MapContainer>
  );
  
  // Add assertions
});

Type-Safe Tile Layer Usage

TypeScript with Tile Layers

import type { TileLayerOptions } from 'leaflet';

interface TileLayerConfig {
  url: string;
  attribution: string;
  options?: TileLayerOptions;
}

const tileConfigs: TileLayerConfig[] = [
  {
    url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    attribution: '&copy; OpenStreetMap',
    options: {
      maxZoom: 19,
      minZoom: 0,
    },
  },
];

// Usage
{tileConfigs.map((config, idx) => (
  <TileLayer
    key={idx}
    url={config.url}
    attribution={config.attribution}
    {...config.options}
  />
))}

Accessibility Considerations

Tile Layer Attribution

Always include proper attribution for map tiles to comply with tile provider terms and help users understand data sources:

<TileLayer
  url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
  attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
  // Attribution is important for legal compliance and transparency
/>

Advanced Patterns

Dynamic Tile URL Based on Time

function WeatherMap() {
  const [timestamp, setTimestamp] = useState(new Date().toISOString());

  useEffect(() => {
    const interval = setInterval(() => {
      setTimestamp(new Date().toISOString());
    }, 300000); // Update every 5 minutes

    return () => clearInterval(interval);
  }, []);

  return (
    <MapContainer center={[51.505, -0.09]} zoom={13}>
      <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
      <WMSTileLayer
        url="https://weather.example.com/wms"
        params={{
          layers: "radar",
          time: timestamp,
          format: "image/png",
          transparent: true,
        }}
        key={timestamp} // Force re-render when timestamp changes
      />
    </MapContainer>
  );
}

Tile Layer with Error Handling

function RobustTileLayer({ url, fallbackUrl }) {
  const [currentUrl, setCurrentUrl] = useState(url);
  const [errorCount, setErrorCount] = useState(0);

  const handleTileError = useCallback(() => {
    setErrorCount(prev => prev + 1);
    
    if (errorCount > 10 && fallbackUrl) {
      console.warn('Switching to fallback tile server');
      setCurrentUrl(fallbackUrl);
    }
  }, [errorCount, fallbackUrl]);

  return (
    <TileLayer
      url={currentUrl}
      eventHandlers={{
        tileerror: handleTileError,
      }}
    />
  );
}

Conditional Tile Layer Based on Zoom

function ZoomDependentTiles() {
  const [zoom, setZoom] = useState(13);

  return (
    <MapContainer center={[51.505, -0.09]} zoom={zoom}>
      <MapEvents onZoomChange={setZoom} />
      
      {zoom < 10 ? (
        <TileLayer url="https://low-detail-tiles.example.com/{z}/{x}/{y}.png" />
      ) : (
        <TileLayer url="https://high-detail-tiles.example.com/{z}/{x}/{y}.png" />
      )}
    </MapContainer>
  );
}

function MapEvents({ onZoomChange }) {
  const map = useMapEvents({
    zoomend: () => {
      onZoomChange(map.getZoom());
    },
  });
  return null;
}