Connecting the API

Usual connection

<!DOCTYPE html>
<html>
    <head>
        <!-- Substitute the value of the real key instead of YOUR_API_KEY -->
        <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
        <script src="index.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
async function initMap() {
    await ymaps3.ready;

    const {YMap, YMapDefaultSchemeLayer} = ymaps3;

    const map = new YMap(
        document.getElementById('app'),
        {
            location: {
                center: [25.229762, 55.289311],
                zoom: 10
            }
        }
    );

    map.addChild(new YMapDefaultSchemeLayer());
}

initMap();
{
    "devDependencies": {
        "http-server": "14.1.1"
    },
    "scripts": {
        "start": "npx http-server ."
    }
}

Install the dependencies and start the local server:

npm install
npm run start

Open application

Alert

The API is guaranteed to work in browsers whose audience is more than 0.5% according to Browserslist.

Specificities of the usual connection

  1. The JS API is distributed exclusively by the link that needs to be connected in the header of the document.
  2. API components are always loaded asynchronously.
  3. Components exist only in the global scope in the variable ymaps3.
  4. Components are available only after the promise ymaps3.ready is resolved.

API loading parameters

apikey

Required parameter

The key issued in the Developer's Dashboard.

Note

Key activation takes up to 15 minutes.

The JS API 3.0 works only with the keys with the "Restriction by HTTP Referer" field filled in. See configuring restrictions

lang

Required parameter

Locale. Set as lang=language_region, where

Promise ready

The promise ymaps3.ready guarantees that all components of the main module Javascript API are loaded, and the DOM is built.

Alert

Event handlers like document.ready, window.onload, jQuery.ready do not signal the end of loading JS API components.

Connecting with Webpack

  • Is it possible not to connect the JS API in the header of the HTML document?
  • Is it possible to call JS API components not from a global variable, but more habitually, as if they are supplied via an npm package?
  • Is it possible to make it so that you don't have to process the resolving of the promise `ymaps3.ready'?

Using Webpack and its [externals] option (https://webpack.js.org/configuration/externals /) this can be done. There are three ways, each has both advantages and limitations.

Method 1

<!DOCTYPE html>
<html>
    <head>
        <!-- Substitute the value of the real key instead of YOUR_API_KEY -->
        <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
        <script src="build/bundle.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
import * as ymaps3 from 'ymaps3';

async function initMap() {
  await ymaps3.ready;

  const {YMap, YMapDefaultSchemeLayer} = ymaps3;

  const map = new YMap(
      document.getElementById('app'),
      {
          location: {
              center: [25.229762, 55.289311],
              zoom: 10
          }
      }
  );

  map.addChild(new YMapDefaultSchemeLayer());
}

initMap();
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
    },
    externals: {
        ymaps3: 'ymaps3'
    },
    devtool: 'cheap-source-map'
};
{
    "devDependencies": {
        "http-server": "14.1.1",
        "webpack": "5.88.2",
        "webpack-cli": "5.1.4"
    },
    "scripts": {
        "build": "webpack",
        "start": "npx http-server ."
    }
}

Install the dependencies, build the project and run the local server:

npm install
npm run build
npm run start

Open application

Specificities

  1. On the HTML page, we are still connecting the JS API loader:

    <head>
        <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
    </head>
    

    Successful loading of the script ensures that the ymaps3 variable appears in global access.

  2. In webpack.config.js declaring an external variable ymaps3:

    module.exports = {
        // ...
        externals: {
            ymaps3: 'ymaps3'
        }
    };
    

    Thanks to this, the project code makes it possible to import ymaps3 as if the ymaps3 code is delivered not through a global variable, but through an npm package:

    import * as ymaps3 from 'ymaps3';
    // ...
    

Method 2

<!DOCTYPE html>
<html>
    <head>
        <script src="build/bundle.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
import * as ymaps3 from 'ymaps3';

async function initMap() {
  await ymaps3.ready;

  const {YMap, YMapDefaultSchemeLayer} = ymaps3;

  const map = new YMap(
      document.getElementById('app'),
      {
          location: {
              center: [25.229762, 55.289311],
              zoom: 10
          }
      }
  );

  map.addChild(new YMapDefaultSchemeLayer());
}

initMap();

const path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
    },
    externalsType: 'script',
    externals: {
        // Substitute the value of the real key instead of YOUR_API_KEY
        ymaps3: ['https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US', 'ymaps3']
    },
    devtool: 'cheap-source-map'
};
{
    "devDependencies": {
        "http-server": "14.1.1",
        "webpack": "5.88.2",
        "webpack-cli": "5.1.4"
    },
    "scripts": {
        "build": "webpack",
        "start": "npx http-server ."
    }
}

Install the dependencies, build the project and run the local server:

npm install
npm run build
npm run start

Open application

Specificities

This is a modified first method:

  1. It is no longer necessary to connect the tag <script> in the header of the HTML page. This is done by webpack itself.

  2. In webpack.config.js the external variable ymaps3 is also declared, but it specifies the path to the API loader:

    module.exports = {
        // ...
        externalsType: 'script',
        externals: {
            ymaps3: ['https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US', 'ymaps3']
        }
    };
    

Limitations of method 1 and 2

In both of the above methods, not a ready-made API is imported, but a lightweight loader. The components become fully accessible only after the ymaps3.ready promise is resolved. This means that if your client code is divided into its own modules, and the JS API is used in several of your modules at once, you should wait for the ymaps3.ready promise to be resolved in each of your modules.

For example, you want to display two maps on a page at once, each in its own container. It is more convenient for you to describe this functionality not in one large module, but in two different ones, for example with the names module-a.js and modile-b.js. Then using the methods described above, the code of your modules will look something like this:

// module-a.js
import * as ymaps3 from 'ymaps3';

async function initMap() {
    // Wait for the resolving ymaps3.ready
    await ymaps3.ready;

    const {YMap} = ymaps3;

    // The map is initialized in the first container
    const map = new YMap({document.getElementById('first-map-container')});
}

initMap();
// module-b.js
import * as ymaps3 from 'ymaps3';

async function initMap() {
    // Wait for the resolving ymaps3.ready
    await ymaps3.ready;

    const {YMap} = ymaps3;

    // The map is initialized in the first container
    const map = new YMap({document.getElementById('second-map-container')});
}

initMap();

We have to wait for the ymaps3.ready resolving in each module. This is inconvenient, I would like to write code easier:

// module-a.js
import * as ymaps3 from 'ymaps3';

const {YMap} = ymaps3;
const map = new YMap({document.getElementById('first-map-container')});
// module-b.js
import * as ymaps3 from 'ymaps3';

const {YMap} = ymaps3;
const map = new YMap({document.getElementById('second-map-container')});

To achieve this, use the third method.

Method 3

<!DOCTYPE html>
<html>
    <head>
        <script src="build/bundle.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
import * as ymaps3 from 'ymaps3';

const {YMap, YMapDefaultSchemeLayer} = ymaps3;

const map = new YMap(
  document.getElementById('app'),
  {
      location: {
          center: [25.229762, 55.289311],
          zoom: 10
      }
  }
);

map.addChild(new YMapDefaultSchemeLayer());
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
    },
    externalsType: 'script',
    externals: {
        // Substitute the value of the real key instead of YOUR_API_KEY
        ymaps3: [
          `promise new Promise((resolve) => {
              if (typeof ymaps3 !== 'undefined') {
                return ymaps3.ready.then(() => resolve(ymaps3));
              }
              const script = document.createElement('script');
              script.src = "https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US";
              script.onload = () => {
                ymaps3.ready.then(() => resolve(ymaps3));
              };
              document.head.appendChild(script);
            })`
        ]
    },
    devtool: 'cheap-source-map'
};
{
    "devDependencies": {
        "http-server": "14.1.1",
        "webpack": "5.88.2",
        "webpack-cli": "5.1.4"
    },
    "scripts": {
        "build": "webpack",
        "start": "npx http-server ."
    }
}

Install the dependencies, build the project and run the local server:

npm install
npm run build
npm run start

Open application

Specificities

This is almost the same method #2, only in webpack.config.js when declaring the variable ymaps3, the path to the API loader is specified somewhat differently and the promise ymaps3.ready is resolved there.

With this connection, fully loaded JS API modules become available in the assembled project js file and the execution of ymaps3.ready is guaranteed, so the project code can be written shorter and neater:

import * as ymaps3 from 'ymaps3';

const {YMap} = ymaps3;
const map = new YMap({...});

Alert

With this connection, the project code will not start executing until the JS API components are fully loaded.

Limitations of method 3

For example, you want to show the loading animation until the map components have loaded. We connected the JS API in method #3 and wrote this code for this. Unfortunately, you will not be able to complete the task:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as ymaps3 from 'ymaps3';

// Execution of this line will start only after loading ymaps3 and the ymaps3.ready resolving
const reactify = ymaps3.reactify.bindTo(React, ReactDOM);
const {YMap} = reactify.module(ymaps3);

function MapView() {
    const [loading, setLoading] = React.useState(true);

    React.useEffect(() => {
        setLoading(false);
    }, []);

    // This code is useless. Of course, it will be executed when mounting the component in the DOM, but
    // mounting will happen only after loading ymaps3 and resolving ymaps3.ready.
    // Therefore, when mounting, the user will see the status 'Loading...' for a fraction of a second,
    // and then useEffect will immediately happen, which will cancel the loading animation.
    if (loading) {
        return <div>Loading...</div>;
    }

    return <YMap />;
}

function App() {
    return (
        <MapView />
    );
}

The main disadvantage of webpack connection #3 is that nothing will be executed in the project modules until the JS API components are fully loaded.

To show the loading animation, method #3 will have to be abandoned:

  1. Switch to webpack connection #1 or #2.
  2. Inside React.useEffect, you need to wait for ymaps3.ready and only then uncheck loading.
  3. The code using the reactify module must be placed inside the MapView component so that the JS interpreter can "read" these lines only after the ymaps3.ready resolving:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as ymaps3 from 'ymaps3';

function MapView() {
    const [loading, setLoading] = React.useState(true);

    React.useEffect(() => {
        ymaps3.ready.then(() => setLoading(false));
    }, []);

    if (loading) {
        return <div>Loading...</div>;
    }

    const reactify = ymaps3.reactify.bindTo(React, ReactDOM);
    const {YMap} = reactify.module(ymaps3);

    return <YMap />;
}

function App() {
    return (
        <MapView />
    );
}

Language switching

Remember that when connecting the API, language is specified. If you are creating international sites, make sure that you switch it with any connection method.

For example, to connect with Webpack in the third way, you can do this:

module.exports = {
  //...
  externals: {
    ymaps3: [
      `promise new Promise((resolve) => {
          ...
          const lang = ['tr', 'tr-TR'].includes(navigator.language) ? 'tr-TR' : 'en-US';
          script.src = "https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=" + lang;
          ...
        })`
    ]
  }
};

Connecting packages and modules

The JS API provides additional packages and modules that are useful for solving specific tasks. The ymaps3.import method is provided for uploading them. Read more in the documentation of packages and modules.