Integration with Next.js

Open in CodeSandbox

This article describes the integration with App Router for Next.js version 13 and above.

Note

Integration with Next.js is based on using the @mappable-world/mappable-reactify module.

Before reading this article, we recommend familiarizing yourself with the integration with React JS.

Step 1. Connecting the API

First, you need to connect the API using the <Script /> component:

import Script from 'next/script';

export default function RootLayout({children}) {
  return (
    <html>
      <body>
        {children}
        <Script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US" strategy="beforeInteractive" />
      </body>
    </html>
  );
}

Warning

It is important to specify the script loading strategy beforeInteractive.

This will allow the API to load before Next.js begins page hydration.

Step 2. Connecting the @mappable-world/mappable-reactify module

In App Router, each page is a server component, the code for which is not executed in the browser.
Let's create a Map component and add the use client directive so that the component's code executes in the browser.

'use client';

const Map = () => {
  return null;
};

export default Map;

Now the component's code is executed twice: in the browser and on the server.
The @mappable-world/mappable-reactify module does not support server rendering.
To ensure the module loads only in the browser, we use React.useEffect.

'use client';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

const Map = () => {
  const [reactifiedApi, setReactifiedApi] = React.useState();

  React.useEffect(() => {
    Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]).then(([{reactify}]) =>
      setReactifiedApi(reactify.bindTo(React, ReactDOM).module(mappable))
    );
  }, []);

  return null;
};

export default Map;

Now we can use @mappable-world/mappable-reactify for rendering the map.

'use client';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

const Map = () => {
    const [reactifiedApi, setReactifiedApi] = React.useState();

    React.useEffect(() => {
        Promise.all([
          mappable.import('@mappable-world/mappable-reactify'),
          mappable.ready
        ]).then(([{reactify}]) =>
            setReactifiedApi(reactify.bindTo(React, ReactDOM).module(mappable))
        );
    }, []);

    if (!reactifiedApi) {
        return null;
    }

    const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer} = reactifiedApi;

    return (
        <MMap location={{center: [25.229762, 55.289311], zoom: 9}}>
            <MMapDefaultSchemeLayer />
            <MMapDefaultFeaturesLayer />
        </MMap>
    );
};

export default Map;
'use client';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {ReactifiedModule} from '@mappable-world/mappable-types/reactify/reactify';
import {LOCATION} from '../variables';

type ReactifiedApi = ReactifiedModule<typeof mappable>;

const Map = () => {
  const [reactifiedApi, setReactifiedApi] = React.useState<ReactifiedApi>();

  React.useEffect(() => {
    Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]).then(([{reactify}]) =>
      setReactifiedApi(reactify.bindTo(React, ReactDOM).module(mappable))
    );
  }, []);

  if (!reactifiedApi) {
    return null;
  }

  const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer} = reactifiedApi;

  return (
    <MMap location={LOCATION}>
      <MMapDefaultSchemeLayer />
      <MMapDefaultFeaturesLayer />
    </MMap>
  );
};

export default Map;
import * as React from 'react';
import Script from 'next/script';
import Map from '@/components/map';

export default function Home() {
  return (
    <>
      <Script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US" strategy="beforeInteractive" />
      <div style={{width: '100svw', height: '100svh'}}>
        <Map />
      </div>
    </>
  );
}
import * as React from 'react';

export default function RootLayout({
  children
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html>
      <body style={{padding: 0, margin: 0}}>{children}</body>
    </html>
  );
}
import type {MMapLocationRequest} from '@mappable-world/mappable-types';

export const LOCATION: MMapLocationRequest = {
  center: [55.44279, 25.24613], // starting position [lng, lat]
  zoom: 9 // starting zoom
};