React

Warning

Supported React version: 16 or higher.

There is a React analog for every imperative API class inherited from YMapEntity. To use the React API version, connect the @yandex/ymaps3-reactify module:

const ymaps3Reactify = await ymaps3.import('@yandex/ymaps3-reactify');
const reactify = ymaps3Reactify.reactify.bindTo(React, ReactDOM);
const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = reactify.module(ymaps3);

Note

The @yandex/ymaps3-reactify module provides a set of React access methods both for individual objects and modules/packages as a whole. The hierarchy of objects and initialization parameters are the same for most classes.

After connecting the module, use YMapEntity descendant objects as React components:

<YMap location={{center: [37.588144, 55.733842], zoom: 9}} mode="vector">
  <YMapDefaultSchemeLayer />
  <YMapDefaultFeaturesLayer />

  <YMapMarker coordinates={[25.229762, 55.289311]} draggable={true}>
    <section>
      <h1>You can drag this header</h1>
    </section>
  </YMapMarker>
</YMap>

Note

The JS API supports integration only with React JS.

React Native is currently not supported.

Quick start

Connecting via top-level-await

<!DOCTYPE html>
<html>
  <head>
    <!-- Replace YOUR_API_KEY with the real key -->
    <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(<App />);
import {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker, reactify} from './lib/ymaps3';
import type {YMapLocationRequest} from 'ymaps3';

const LOCATION: YMapLocationRequest = {
  center: [37.588144, 55.733842],
  zoom: 9
};

export default function App() {
  return (
    <div style={{width: '600px', height: '400px'}}>
      <YMap location={reactify.useDefault(LOCATION)}>
        <YMapDefaultSchemeLayer />
        <YMapDefaultFeaturesLayer />

        <YMapMarker coordinates={reactify.useDefault([37.588144, 55.733842])} draggable={true}>
          <section>
            <h1>You can drag this header</h1>
          </section>
        </YMapMarker>
      </YMap>
    </div>
  );
}
import React from 'react';
import ReactDom from 'react-dom';

const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);

export const reactify = ymaps3React.reactify.bindTo(React, ReactDom);
export const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = reactify.module(ymaps3);
{
  "compilerOptions": {
    "target": "es2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "react-jsx",
    "typeRoots": ["./node_modules/@types", "./node_modules/@yandex/ymaps3-types"],
    "paths": {
      "ymaps3": ["./node_modules/@yandex/ymaps3-types"]
    }
  }
}
{
  "devDependencies": {
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@yandex/ymaps3-types": "^0.0.17",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-scripts": "5.0.1",
    "typescript": "^4.9.5"
  },
  "scripts": {
    "start": "react-scripts start"
  }
}

Set dependencies and run a local server:

npm install
npm start

Open the app

Specifics

  1. In package.json, add a dev dependency on the @yandex/ymaps3-types package.

    We recommend installing the latest version:

    npm i --save-dev @yandex/ymaps3-types@latest

  2. In tsconfig.json, set compilerOptions.typeRoots with a list of paths to type files. Add a path to the @yandex/ymaps3-types package there to make the ymaps3 namespace with types appear in the global scope.

    Note

    The ymaps3 namespace contains all the class types provided by the JS API, but they are not available in the runtime environment until ymaps3.ready is resolved.

  3. In tsconfig.json, set compilerOptions.paths, informing the TS compiler that the contents of the imported ymaps3 package should be searched for at the specified path. This enables you to import types in project files as if they were located not at @yandex/ymaps3-types, but in the ymaps3 package:

    import type {YMapLocationRequest} from 'ymaps3';
    

    All types must be imported from the root.

    The internal structure isn't guaranteed and can change over time.

  4. In tsconfig.json, for top-level-await to operate correctly, the compilerOptions.module parameter must be set to one of the following values: es2022, esnext, system, or preserve. The compilerOptions.target parameter must be set to es2017 or higher.

  5. Connect the @yandex/ymaps3-reactify module. In lib/ymaps3.ts, wait until the JS API and Reactify module are fully loaded and then export the necessary map components so that they can be used in other parts of the project:

    import React from 'react';
    import ReactDom from 'react-dom';
    
    const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);
    
    export const reactify = ymaps3React.reactify.bindTo(React, ReactDom);
    export const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = reactify.module(ymaps3);
    

    Note

    The @yandex/ymaps3-reactify module provides a set of React access methods both for individual objects and modules/packages as a whole. The hierarchy of objects and initialization parameters are the same.

  6. The use of top-level-await in lib/ymaps3.ts guarantees that ymaps3.ready and ymaps3.import('@yandex/ymaps3-reactify') are executed before the map components are imported, allowing any JS API objects to be used as React components synchronously:

    <YMap location={reactify.useDefault(LOCATION)}>
      <YMapDefaultSchemeLayer />
      <YMapDefaultFeaturesLayer />
      ...
    </YMap>
    

reactify.useDefault

YMap and all other components are uncontrolled. Components use the imperative interface of the library (for example, YMapZoomControl calls YMap.update({location})). This may cause desynchronizing with the state and parameters in React (for example, location for YMap or coordinates for YMapMarker with dragging enabled).

Use reactify.useDefault(value) to set a component parameter only once and not update it on re-renders. For example, <YMap location={reactify.useDefault({center, zoom})}/> will behave as <input defaultValue={''}/>.

To control parameter updates, use the second reactify.useDefault(value, deps) parameter: an array of dependencies as in React hooks (for example, useCallback, useMemo, useEffect). The parameter will be updated if the dependency array is different.

For parameters with objects, the value itself can be used as a dependency. Re-rendering occurs only if the values are different. For example, const [location, setLocation] = useState(...), reactify.useDefault(location, [location]), setLocation({...}).

reactify.useDefault works with any parameters of any components from reactify.

Warning

reactify.useDefault returns an object with no public contract and should be used only directly in component parameters.

Custom implementations of ymaps3.YMapEntity objects for React

Use the ymaps3.overrideKeyReactify key to determine custom implementations of ymaps3.YMapEntity objects for reactify:

type YMapSomeClassProps = {
  id: string;
};
export class YMapSomeClass extends ymaps3.YMapComplexEntity<YMapSomeClassProps> {
  static [ymaps3.overrideKeyReactify] = YMapSomeClassReactifyOverride;
  //...
}

and the method for determining a custom implementation:

export const YMapSomeClassReactifyOverride = (
  YMapSomeClassI, // base YMapSomeClass class
  {reactify, React}
) => {
  const YMapSomeClassReactified = reactify.entity(YMapSomeClassI); // Standard reactify method
    const YMapSomeClassR = React.forwardRef((props, ref) => {
      return (<>
        <YMapSomeClassReactified {...props} ref={ref} ... />
      </>);
    })
  return YMapSomeClassR;
}

and add the resulting component to the app:

import {YMapSomeClass} from './some-class';
import React from 'react';
import ReactDOM from 'react-dom';
// ...
const ymaps3Reactify = await ymaps3.import('@yandex/ymaps3-reactify');
const reactify = ymaps3Reactify.reactify.bindTo(React, ReactDOM);
const YMapSomeClassR = reactify.entity(YMapSomeClass);

function App() {
  return <YMapSomeClassR id="some_id" />;
}
Next