Mobile-friendly interactive maps powered by Leaflet.js.
Interactive map component is powered by Leaflet React plugin.
Tiles layer (actual map image) is provided by Maptiler plugin.
Leaflet makes direct calls to the DOM when it is loaded, therefore React Leaflet is not compatible with server-side rendering. That's why all React Leaflet components should be imported to the page using Next dynamic import with disabled server-side rendering.
import dynamic from 'next/dynamic'
const MapContainer = dynamic(() =>
import('react-leaflet').then(mod => mod.MapContainer),
{ ssr: false }
)
const TileLayer = dynamic(() =>
import('react-leaflet').then(mod => mod.TileLayer),
{ ssr: false }
)
import 'leaflet/dist/leaflet.css'
{/* Basic map example (no markers passed) */}
<MapContainer
center={[0, 0]}
zoom={1}
scrollWheelZoom={false} // Enable/disable scroll wheel zooming
style={{height: '400px'}}
>
<TileLayer
url='https://api.maptiler.com/maps/voyager/{z}/{x}/{y}.png?key=YOUR_API_KEY'
// To rgister API key with Maptiler check https://docs.maptiler.com/cloud/api/
tileSize={512}
zoomOffset={-1}
minZoom={1}
attribution={'<a href='https://www.maptiler.com/copyright/' target='_blank'>© MapTiler</a> <a href='https://www.openstreetmap.org/copyright' target='_blank'>© OpenStreetMap contributors</a>'}
/>
</MapContainer>
import dynamic from 'next/dynamic'
const MapContainer = dynamic(() =>
import('react-leaflet').then(mod => mod.MapContainer),
{ ssr: false }
)
const TileLayer = dynamic(() =>
import('react-leaflet').then(mod => mod.TileLayer),
{ ssr: false }
)
const CustomMarker = dynamic(() =>
import('../../components/partials/CustomMarker'),
{ ssr: false }
)
const Popup = dynamic(() =>
import('react-leaflet').then(mod => mod.Popup),
{ ssr: false }
)
import 'leaflet/dist/leaflet.css'
{/* Map featuring custom marker with popup */}
<MapContainer
center={[51.5074, -0.1278]}
zoom={10}
scrollWheelZoom={false} // Enable/disable scroll wheel zooming
style={{height: '400px'}}
>
<TileLayer
url='https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=YOUR_API_KEY'
// To rgister API key with Maptiler check https://docs.maptiler.com/cloud/api/
tileSize={512}
zoomOffset={-1}
minZoom={1}
attribution={'<a href='https://www.maptiler.com/copyright/' target='_blank'>© MapTiler</a> <a href='https://www.openstreetmap.org/copyright' target='_blank'>© OpenStreetMap contributors</a>'}
/>
<CustomMarker position={[51.5074, -0.1278]}>
<Popup>
<div className='p-3'>
<h6>Hi, I'm in London</h6>
<p className='fs-sm pt-1 mt-n3 mb-0'>Lorem ipsum dolor sit amet elit.</p>
</div>
</Popup>
</CustomMarker>
</MapContainer>
import dynamic from 'next/dynamic'
const MapContainer = dynamic(() =>
import('react-leaflet').then(mod => mod.MapContainer),
{ ssr: false }
)
const TileLayer = dynamic(() =>
import('react-leaflet').then(mod => mod.TileLayer),
{ ssr: false }
)
const CustomMarker = dynamic(() =>
import('../../components/partials/CustomMarker'),
{ ssr: false }
)
const Popup = dynamic(() =>
import('react-leaflet').then(mod => mod.Popup),
{ ssr: false }
)
import 'leaflet/dist/leaflet.css'
{/* Map featuring multiple dot markers with popups */}
<MapContainer
center={[40.7128, -74.0060]}
zoom={11}
scrollWheelZoom={false} // Enable/disable scroll wheel zooming
style={{height: '400px'}}
>
<TileLayer
url='https://api.maptiler.com/maps/pastel/{z}/{x}/{y}.png?key=YOUR_API_KEY'
// To rgister API key with Maptiler check https://docs.maptiler.com/cloud/api/
tileSize={512}
zoomOffset={-1}
minZoom={1}
attribution={'<a href='https://www.maptiler.com/copyright/' target='_blank'>© MapTiler</a> <a href='https://www.openstreetmap.org/copyright' target='_blank'>© OpenStreetMap contributors</a>'}
/>
<CustomMarker position={[40.702, -74.0068]} icon='dot'>
<Popup>
<div className='p-3'>
<h6>Hi, I'm in New York</h6>
<p className='fs-sm pt-1 mt-n3 mb-0'>Lorem ipsum dolor sit amet elit.</p>
</div>
</Popup>
</CustomMarker>
<CustomMarker position={[40.716, -74.078]} icon='dot'>
<Popup>
<div className='p-3'>
<h6>Hi, I'm in Jersey Cty</h6>
<p className='fs-sm pt-1 mt-n3 mb-0'>Lorem ipsum dolor sit amet elit.</p>
</div>
</Popup>
</CustomMarker>
<CustomMarker position={[40.650, -74.209]} icon='dot'>
<Popup>
<div className='p-3'>
<h6>Hi, I'm in Elizabeth</h6>
<p className='fs-sm pt-1 mt-n3 mb-0'>Lorem ipsum dolor sit amet elit.</p>
</div>
</Popup>
</CustomMarker>
</MapContainer>
import dynamic from 'next/dynamic'
const MapContainer = dynamic(() =>
import('react-leaflet').then(mod => mod.MapContainer),
{ ssr: false }
)
const TileLayer = dynamic(() =>
import('react-leaflet').then(mod => mod.TileLayer),
{ ssr: false }
)
const CustomMarker = dynamic(() =>
import('../../components/partials/CustomMarker'),
{ ssr: false }
)
const Popup = dynamic(() =>
import('react-leaflet').then(mod => mod.Popup),
{ ssr: false }
)
import 'leaflet/dist/leaflet.css'
{/* Markers array */}
const markers = [
{
position: [52.52, 13.407],
iconUrl: '/images/map/marker-accomodation.png',
iconSize: [48, 48],
popup: {
href: '#',
img: '/images/city-guide/catalog/02.jpg',
title: 'Grand Resort & Spa',
rating: 4.6,
reviews: 43,
address: 'Ollenhauer Str. 29, 10118, Berlin',
price: '$$$'
}
},
{
position: [52.51, 13.36],
iconUrl: '/images/map/marker-fitness.png',
iconSize: [48, 48],
popup: {
href: '#',
img: '/images/city-guide/catalog/16.jpg',
title: 'Power Fitness Club',
rating: 4.2,
reviews: 317,
address: 'Genslerstraße 84, 10118, Berlin',
price: '$$'
}
},
{
position: [52.528, 13.37],
iconUrl: '/images/map/marker-cafe.png',
iconSize: [48, 48],
popup: {
href: '#',
img: '/images/city-guide/catalog/17.jpg',
title: 'ClubFood Restaurant & Bar',
rating: 4.7,
reviews: 239,
address: 'Antwerpener str. 47, 13253, Berlin',
price: '$$'
}
},
{
position: [52.525, 13.45],
iconUrl: '/images/map/marker-nightlife.png',
iconSize: [48, 48],
popup: {
href: '#',
img: '/images/city-guide/catalog/18.jpg',
title: 'Egoist Night Club & Bar',
rating: 4.8,
reviews: 117,
address: 'Kochhannstraße str. 32, 11060, Berlin',
price: '$$'
}
},
{
position: [52.503, 13.409],
iconUrl: '/images/map/marker-meds.png',
iconSize: [48, 48],
popup: {
href: '#',
img: '/images/city-guide/catalog/19.jpg',
title: 'Fair Meds Pharmacy',
rating: 4.9,
reviews: 561,
address: 'Lansstraße 81, D-11179, Berlin',
price: '$'
}
},
{
position: [52.54, 13.403],
iconUrl: '/images/map/marker-shopping.png',
iconSize: [48, 48],
popup: {
href: '#',
img: '/images/city-guide/catalog/20.jpg',
title: 'Fetishist Shopping Mall',
rating: 4.3,
reviews: 1274,
address: 'Mellingburgredder 3, 13250, Berlin',
price: '$$'
}
}
]
{/* Map featuring multiple custom markers with popups passed via markers array */}
<MapContainer
center={[52.53, 13.405]}
zoom={12}
scrollWheelZoom={false}
style={{height: '600px'}}
>
<TileLayer
url='https://api.maptiler.com/maps/pastel/{z}/{x}/{y}.png?key=BO4zZpr0fIIoydRTOLSx'
tileSize={512}
zoomOffset={-1}
minZoom={1}
attribution={'<a href='https://www.maptiler.com/copyright/' target='_blank'>© MapTiler</a> <a href='https://www.openstreetmap.org/copyright' target='_blank'>© OpenStreetMap contributors</a>'}
/>
{markers.map((marker, indx) => {
return <CustomMarker
key={indx}
position={marker.position}
icon='custom'
iconOptions={{
url: marker.iconUrl,
size: [marker.iconSize[0], marker.iconSize[1]],
}}
>
<Popup>
<Link href={marker.popup.href} className='d-block'>
<ImageLoader src={marker.popup.img} width={280} height={128} alt='Image' />
</Link>
<div className='card-body'>
<h5 className='card-title fs-base'>
<Link href={marker.popup.href} className='nav-link'>{marker.popup.title}</Link>
</h5>
<div className='d-flex align-items-center mb-2'>
<StarRating rating={marker.popup.rating} className='mt-n1 me-2' />
<span className='fs-xs'>
<strong>{marker.popup.rating}</strong>
<span className='text-muted ms-1'>({marker.popup.reviews})</span>
</span>
</div>
<div className='mb-2'>
<i className='fi-map-pin text-muted fs-sm mt-n1 me-1'></i>
{marker.popup.address}
</div>
<i className='fi-credit-card text-muted fs-sm mt-n1 me-1'></i>
{marker.popup.price}
</div>
</Popup>
</CustomMarker>
})}
</MapContainer>