https://github.com/PaulLeCam/react-leaflet#technical-considerations have the following point:
Leaflet makes direct calls to the DOM when it is loaded, therefore this library is not compatible with server-side rendering.
I would like to discuss of the possibilities how it's possible to add server-side support for this library.
My current solution just skip rendering the leaflet on server-side, and render just on client-side:
render() {
const position = [51.505, -0.09];
if (process.env.BROWSER) {
var {Map, Marker, Popup, TileLayer} = require('react-leaflet');
return (
<div className="search-map">
<Map center={position} zoom={13}>
<TileLayer
url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
/>
<Marker position={position}>
<Popup>
<span>A pretty CSS3 popup.<br/>Easily customizable.</span>
</Popup>
</Marker>
</Map>
</div>
);
}
else {
return null;
}
}
I don't believe it's a best practice, but still it's something, than nothing, and I think it's better than using the headless-leaflet fork.
The issue is with Leaflet itself, as it doesn't check if the DOM is available when it's loaded, not directly this library.
I do not plan to support server-side rendering in this lib until it is supported by Leaflet itself, unless it's a trivial implementation with no side-effect.
The solution you presented may work for you, but it's completely dependant on your environment and your build, it would probably not work in other cases.
@PaulLeCam We could provide a React Component as fallback for server side rendering, check if window exists render the map or render the fallback? Something like that:
<Map .... fallback={ReactComponent}>
...
</Map>
@iam4x Feel free to implement any workaround in your app... As I previously explained, this issue is not caused by this lib.
Thanks for the workarounds gents, I ended up just using a client side bower package and not the react package, but will switch back now I know the render could be defined conditionally based on the environment
@dougajmcdonald similar idea I'm using too. It's more flexible and maintainable.
@dougajmcdonald I was wondering what was the solution for this? You guys are loading leaflet in with bower, and then proceeding to require react-leaflet the same way? Or are you guys also using bower to load react-leaflet?
May someone give the example of this workaround
It's ugly, but it works.
let Map, MapComponents
class LeafletMap extends Component {
componentDidMount(){
//Only runs on Client, not on server render
Map = require('react-leaflet').Map
MapComponents = require('./mapComponents').default
this.forceUpdate()
}
render () {
return (
(Map)
? (
<Map
zoom={10}
maxZoom={18}
minZoom={9}
>
<MapComponent />
</Map>
)
: (null)
}
)
}
}
For future googlers, there is also https://github.com/masotime/react-leaflet-universal
And some attempts to render leaflet on the server:
it works, too.
In index.html:
Include ..rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"..
In React Component
`import React, { Component } from 'react'
class Mapa extends Component {
componentWillMount() {
console.log('componentWillMount')
Map = require('react-leaflet').Map
TileLayer = require('react-leaflet').TileLayer
TileLayer = require('react-leaflet').TileLayer
Marker = require('react-leaflet').Marker
Popup = require('react-leaflet').Popup
}
render() {
const position = [-12.76767, -76.343434]
return (
<Map center={position} zoom={10} style={{ height: "100vh" }}>
<TileLayer
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
/>
<Marker position={position}>
<Popup>
<span>A pretty CSS3 popup. <br /> Easily customizable.</span>
</Popup>
</Marker>
</Map>
)
}
}
export default Mapa`
The cleanest solution i found is here : https://github.com/etalab/adresse.data.gouv.fr/blob/0bc6dd7/pages/map.js#L5-L12
rktel how to extends MapControl with componentWillMount().
I want add search box by https://github.com/smeijer/leaflet-geosearch.
For anyone wondering I managed to get this working using react-loadable for now
In your parent component do something like so:
const LoadableSearchResultMap = Loadable({
loader: () => import('./SearchResultMap') as Promise<any>,
loading() {
return <div>Loading...</div>
}
});
Then I made a component called SearchResultMap.tsx and inside is:
`import * as React from 'react';
import { Map, Marker, Popup, TileLayer, LayersControl } from 'react-leaflet';
import ReactLeafletGoogleLayer from 'react-leaflet-google-layer';
import { LatLngExpression } from 'leaflet';
export default class SearchResultMap extends React.Component {
mapSettings = {
zoom: 15,
center: [48.067539, 12.862530] as LatLngExpression
}
render() {
return <><Map {...this.mapSettings}><ReactLeafletGoogleLayer googleMapsLoaderConf={{ KEY: 'Key Goes Here' }} type={'terrain'} /></Map></>
}
}`
The cleanest solution i found is here : https://github.com/etalab/adresse.data.gouv.fr/blob/0bc6dd7/pages/map.js#L5-L12
Sorry, but how i pass props with this dynamic
Hopefully this will help someone...If you're using gatsby then for some reason using the react-leaflet gatsby plugin works rather than using the library straight...weird but it saved me a lot of headache.
This might be helpful for those who use Webpack & es6 dynamic import feature:
import React, { PureComponent } from 'react';
class MapLoader extends PureComponent {
constructor(props) {
super(props);
this.state = { loading: true };
this.Map= null;
}
componentDidMount() {
import(/* webpackMode: "eager" */ './Map').then((module) => {
this.Map = module.default;
this.setState({ loading: false });
});
}
render() {
const { Map, props, state: { loading } } = this;
if (loading) {
return null;// or render a loading
}
return <Map {...props} />;
}
}
export default MapLoader;
Most helpful comment
It's ugly, but it works.