Интеграция с Next.js

Данная статья описывает интеграцию с App Router Next.js версии 13 и выше.

Примечание

Интеграция с Next.js основана на использовании модуля @yandex/ymaps3-reactify.

Перед прочтением данной статьи, рекомендуем ознакомиться с интеграцией с React JS.

Шаг 1. Подключение API

Для начала необходимо подключить API с помощью компонента <Script />:

import Script from 'next/script';

export default function RootLayout({children}) {
  return (
    <html>
      <body>
        {children}
        <Script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU" strategy="beforeInteractive" />
      </body>
    </html>
  );
}

Важно

Важно указать стратегию загрузки скрипта beforeInteractive.

Это позволит загрузить API до того, как Next.js начнет гидрацию страницы.

Шаг 2. Подключение модуля @yandex/ymaps3-reactify

В App Router каждая страница является серверным компонентом, код которого не выполняется в браузере.
Создадим компонент Map и добавим директиву use client, чтобы код компонента выполнялся в браузере.

'use client';

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

export default Map;

Теперь код компонента выполняется дважды: в браузере и на сервере.
Модуль @yandex/ymaps3-reactify не поддерживает серверный рендеринг.
Для того, чтобы модуль загружался только в браузере, используем 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([
          ymaps3.import('@yandex/ymaps3-reactify'),
          ymaps3.ready
        ]).then(([{reactify}]) =>
            setReactifiedApi(reactify.bindTo(React, ReactDOM).module(ymaps3))
        );
    }, []);

  return null;
};

export default Map;

Теперь мы можем использовать @yandex/ymaps3-reactify для рендеринга карты.

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

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

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

    if (!reactifiedApi) {
        return null;
    }

    const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = reactifiedApi;

    return (
        <YMap location={{center: [37.588144, 55.733842], zoom: 9}}>
            <YMapDefaultSchemeLayer />
            <YMapDefaultFeaturesLayer />
        </YMap>
    );
};

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

type ReactifiedApi = ReactifiedModule<typeof ymaps3>;

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

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

    if (!reactifiedApi) {
        return null;
    }

    const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = reactifiedApi;

    return (
        <YMap location={LOCATION}>
            <YMapDefaultSchemeLayer />
            <YMapDefaultFeaturesLayer />
        </YMap>
    );
};

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://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"
                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 {YMapLocationRequest} from '@yandex/ymaps3-types';

export const LOCATION: YMapLocationRequest = {
    center: [37.623082, 55.75254], // starting position [lng, lat]
    zoom: 9 // starting zoom
};