admin管理员组

文章数量:1136157

I've got very simple code to display a map using react-leaflet and place a marker on it. However, i get the following two errors in my browser console

GET http://localhost:8080/marker-icon-2x.png 404 (Not Found)

GET http://localhost:8080/marker-shadow.png 404 (Not Found)

I tried to fix this issue by downloading those two images and placing them at the root. It works. However, how can i change the URL the react-leaflet marker element looks for the marker images? I'd like to store them in "./images" rather than at the root.

I've got very simple code to display a map using react-leaflet and place a marker on it. However, i get the following two errors in my browser console

GET http://localhost:8080/marker-icon-2x.png 404 (Not Found)

GET http://localhost:8080/marker-shadow.png 404 (Not Found)

I tried to fix this issue by downloading those two images and placing them at the root. It works. However, how can i change the URL the react-leaflet marker element looks for the marker images? I'd like to store them in "./images" rather than at the root.

Share Improve this question asked Mar 23, 2018 at 2:45 user3059217user3059217 5911 gold badge6 silver badges9 bronze badges 1
  • 2 Can you mark helpfull answer? – Stevan Tosic Commented Jan 7, 2019 at 14:26
Add a comment  | 

12 Answers 12

Reset to default 102

Try to do this :)

React leaflet for some reason do not include images and you will need to reset default icons image.

Below is some working example, I hope it will solve your issue.

You also can add icons from one of public link

https://cdnjs.com/libraries/Leaflet.awesome-markers

import React, { Component } from 'react';
import L from 'leaflet';
import {
    Map, TileLayer, Marker, Popup
} from 'react-leaflet'
import 'leaflet/dist/leaflet.css';
import './style.css';


import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';

let DefaultIcon = L.icon({
    iconUrl: icon,
    shadowUrl: iconShadow
});

L.Marker.prototype.options.icon = DefaultIcon;

Adding answer for Next.js

  1. Copy over marker icon from node_modules/leaflet/dist/images to public/images something like /images/marker-icon.png

  2. Create Leaflet icon reference and use the reference in Marker

const icon = L.icon({ iconUrl: "/images/marker-icon.png" });

// some other code

<Marker key={obj.id} position={position} icon={icon}>

// rest of the code

Here is the solution that worked for me:

I added the following lines in the top of the file:

import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import L from 'leaflet';

delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
    iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png').default,
    iconUrl: require('leaflet/dist/images/marker-icon.png').default,
    shadowUrl: require('leaflet/dist/images/marker-shadow.png').default
});

In 2024 you can just use leaflet-defaulticon-compatibility.

npm install leaflet-defaulticon-compatibility --save

Make sure to import the compatibility package after leaflet!

import 'leaflet/dist/leaflet.css';
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css'; // Re-uses images from ~leaflet package
import L from 'leaflet';
import 'leaflet-defaulticon-compatibility';

The position of your react-leaflet imports should not matter. This setup also works well with NextJS 14.

It seems not all stuff is properly integrated together when using react, leaflet and react-leaflet. I had the same problem and found this

https://github.com/PaulLeCam/react-leaflet/issues/453

You need to setup leafelet itself again, as something brokes after importing leaflet.css.

Hope it helps

Copy all images from leaflet package to the public directory:

cp node_modules/leaflet/dist/images/* {PUBLIC_WEB_DIRECTORY}/leaflet_images/

Fix the path in Leaflet

import L from 'leaflet';
L.Icon.Default.imagePath='leaflet_images/';

Elaborated answer from @ch4nd4n's answer and adapted for leaflet (v1.8.0) for reactjs (v17).

import iconMarker from 'leaflet/dist/images/marker-icon.png'
import iconRetina from 'leaflet/dist/images/marker-icon-2x.png'
import iconShadow from 'leaflet/dist/images/marker-shadow.png'

Then,

const icon = L.icon({ 
    iconRetinaUrl:iconRetina, 
    iconUrl: iconMarker, 
    shadowUrl: iconShadow 
});

Add icon prop to the Marker component.

<Marker key={index} position={[loc.lat, loc.long]} icon={icon}>
    <Popup><h3>{loc.name}</h3> {loc.address}</Popup>
</Marker>

Similar solution to @Daniel-James But for TypeScript.

//CSS and marker image fix for Leaflet map
import "leaflet/dist/leaflet.css";
import iconMarker from "leaflet/dist/images/marker-icon.png";
import iconRetina from "leaflet/dist/images/marker-icon-2x.png";
import iconShadow from "leaflet/dist/images/marker-shadow.png";
import L from "leaflet";
L.Icon.Default.mergeOptions({
  iconRetinaUrl: iconRetina,
  iconUrl: iconMarker,
  shadowUrl: iconShadow,
});

Add this code to the imports section of your script.

Explanation

As far as I understand, Leaflet is typically used from CDN, but in the case of using react we'll be using node modules instead. Therefore we need to re-point the CSS to that of the node module, and also repoint the default icons to point towards the node module too.

What ended up fixing this for me was removing:

import 'leaflet/dist/leaflet.css';

from the file in which my map components was in. I ended up importing leaflet css through the create-react-app index.html file and my marker was able to load alongside my map. Hope this helps anyone stuck.

Because I was unable to find a full example that solved all of my issues, I'm posting my code. Working in January 2024 with the latest versions of leaflet, react-leaflet, react, and next.js. This example should allow the client to load markers (from the CDN), but also refresh the page even if it's being delivered from server-side contexts like next.js.

import React, { useEffect, useState } from 'react';

export const Map = ({ mapMarkers, height }) => {
  const [Leaflet, setLeaflet] = useState(null);
  const [MapContainer, setMapContainer] = useState(null);
  const [TileLayer, setTileLayer] = useState(null);
  const [Marker, setMarker] = useState(null);
  const [Popup, setPopup] = useState(null);

  useEffect(() => {
    import('react-leaflet').then((mod) => setMapContainer(mod.MapContainer));
    import('react-leaflet').then((mod) => setTileLayer(mod.TileLayer));
    import('react-leaflet').then((mod) => setMarker(mod.Marker));
    import('react-leaflet').then((mod) => setPopup(mod.Popup));
    import('leaflet').then((mod) => setLeaflet(mod));
  }, []); // Run imports just once on mount

  const [customIcon, setCustomIcon] = useState(null);
  const [initialBounds, setInitialBounds] = useState(null);

  useEffect(() => {
    if (Leaflet && mapMarkers.length > 0) {
      // Calculate the bounds based on marker positions
      const markerBounds = mapMarkers.map(({ position }) => position);
      const bounds = Leaflet.latLngBounds(markerBounds);

      // Pad the bounds to add some padding to the viewport
      const paddedBounds = bounds.pad(1); // Adjust the padding factor as needed

      // Set initial bounds for the MapContainer
      setInitialBounds(paddedBounds);

      // Initialize Leaflet once the component is mounted on the client side, and provide icons.
      const icon = new Leaflet.Icon({
        iconUrl: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/images/marker-icon.png',
        iconSize: [22, 32],
        iconAnchor: [16, 32],
        popupAnchor: [0, -32],
      });

      setCustomIcon(icon);
    }
  }, [Leaflet, mapMarkers]); // Run the effect whenever Leaflet or mapMarkers are updated

  
  return (
    MapContainer && initialBounds && (
      <MapContainer
        bounds={initialBounds}
        style={{ width: '100%', height: height || '400px' }}
      >
        <TileLayer
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        />
        {customIcon &&
          mapMarkers.map(({ position, popupContent }, index) => (
            <Marker key={index} position={position} icon={customIcon}>
              <Popup>{popupContent}</Popup>
            </Marker>
          ))}
      </MapContainer>
    )
  );
};

The component employs useEffect hooks to avoid Server Side Rendering. Because of the dependency on Leaflet.js, the map cannot be rendered server-side. Leaflet was built, and continues to be developed, for the context of rendering maps in standard browser environments. In such contexts, it is expected that Leaflet will have access to the Window object. This is categorically untrue in server side contexts, and so this component falls back on Client Side Rendering, using a CDN to deliver static assets (as Leaflet expects).

I have not taken the time to add types; please feel free to make that effort if you're so moved!

For anyone attempting to use leaflet in an expo react native app, the marker image also won't load. A workaround is to use expo-asset, npx expo install expo-asset, and then place an icon in the assets folder. Then you'll need to get a uri using expo-asset and use that as the iconUrl for the leaflet icon.

import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import { Asset } from "expo-asset";

import React from "react";
import { StyleSheet } from "react-native";

const WebMap = () => {
  const icon = require('../../../assets/icon.png');
  const iconURI = Asset.fromModule(icon).uri;

  const leafletIcon = new L.Icon({
    iconUrl: iconURI,
    iconSize: [30, 30],
    iconAnchor: [22, 94],
    popupAnchor: [-3, -76],
  });
  return (
      <MapContainer
        center={[51.505, -0.09]}
        zoom={13}
        scrollWheelZoom={true}
        style={styles.container}
      >
        <TileLayer
          url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        />
        <Marker position={[51.505, -0.09]} icon={leafletIcon}>
          <Popup>
            A pretty CSS3 popup. <br /> Easily customizable.
          </Popup>
        </Marker>
      </MapContainer>
  );
};

const styles = StyleSheet.create({
  container: {
    width: "500px",
    height: "500px",
  },
});

export default WebMap;

The easiest solution, the best:

import Leaflet from 'leaflet';
Leaflet.Icon.Default.imagePath = '/images/'

本文标签: javascriptReactLeaflet marker files not foundStack Overflow