Connecting the API using JavaScript

Note

Get a key for the JavaScript API package

The key is activated within 15 minutes after you receive it.

Thr JS API 3.0 works only with the keys with the "Restriction by HTTP Referer" field filled in. Learn more about the restrictions

Regular connection

<!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>
        <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: [37.588144, 55.733842],
                zoom: 10
            }
        }
    );

    map.addChild(new YMapDefaultSchemeLayer());
}

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

Set dependencies and run a local server:

npm install
npm run start

Open the app

Alert

The API is guaranteed to work with browsers that have an audience coverage of at least 0.5% according to Browserslist.

Specifics of regular connection

  1. The JS API is distributed exclusively via a link that should be inserted in the document header.
  2. API components are always loaded asynchronously.
  3. Components exist only in the global scope in the ymaps3 variable.
  4. Components are available only after the ymaps3.ready promise is resolved.

API loading parameters

apikey

Required parameter

The key issued in the Developer's Dashboard.

Note

The key is activated within 15 minutes after you receive it.

Thr JS API 3.0 works only with the keys with the "Restriction by HTTP Referer" field filled in. Learn more about the restrictions

lang

Required parameter

Locale, set aslang=language_region:

ready promise

The ymaps3.ready promise guarantees that all components of the core JavaScript API module are loaded and the DOM is built.

Alert

Handlers of events like document.ready, window.onload, or jQuery.ready do not signal the end of JS API component loading.

Connecting via top-level-await (recommended)

<!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>
    <script type="module" src="index.js"></script>
  </head>
  <body>
    <div id="app" style="width: 600px; height: 400px"></div>
  </body>
</html>
import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps3.js'

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

map.addChild(new YMapDefaultSchemeLayer());
await ymaps3.ready;

export const {YMap, YMapDefaultSchemeLayer} = ymaps3;
{
  "devDependencies": {
    "http-server": "14.1.1"
  },
  "scripts": {
    "start": "npx http-server ."
  }
}

Set dependencies and run a local server:

npm install
npm run start

Open the app

Specifics

  1. Specify the type="module" attribute in the "script" tag that loads the compiled project JS to enable ECMAScript Modules (ESM) and top-level-await support:

    <script type="module" src="index.js"></script>
    
    ESM support when using bundlers

    If you're building a project using a bundler (for example, Webpack), add "type": "module" to package.json

  2. In lib/ymaps3.js, wait until the JS API is fully loaded and then export the necessary map components so that they can be used in other parts of the project:

    await ymaps3.ready;
    
    export const {YMap, YMapDefaultSchemeLayer} = ymaps3;
    
  3. The use of top-level-await in lib/ymaps3.js guarantees that ymaps3.ready is executed before the map components are imported, so the project code can be shorter and cleaner (the asynchronous initMap function is no longer needed):

    import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps3.js'
    
    const map = new YMap({...});
    

Connecting with Webpack

  • Is there a way to not connect the JS API in the HTML document header?
  • Can JS API components be called not from a global variable, but more practically, as if they were delivered via an npm package?
  • Is there a way to skip handling the ymaps3.ready promise resolution?

You can do this using Webpack and its externals option. There are three methods, each with its own advantages and limitations.

Method 1 (without the ymaps3global variable)

<!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>
        <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: [37.588144, 55.733842],
              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 ."
    }
}

Set dependencies, build a project, and run a local server:

npm install
npm run build
npm run start

Open the app

Specifics

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

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

    Successful script loading guarantees that the ymaps3 variable becomes globally accessible.

  2. In webpack.config.js, declare the external ymaps3 variable:

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

    This makes it possible to import ymaps3 in the project code as if the ymaps3 code were delivered not via a global variable, but via an npm package:

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

Method 2 (without the JS APIloader)

<!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: [37.588144, 55.733842],
              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: {
        // replace YOUR_API_KEY with the real 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 ."
    }
}

Set dependencies, build a project, and run a local server:

npm install
npm run build
npm run start

Open the app

Specifics

This is a modification of the first method:

  1. You no longer need to connect the <script> tag in the HTML document header — Webpack does this for you.

  2. In webpack.config.js, you also need to declare the ymaps3 variable, but it should contain a 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 methods 1 and 2

In both of the above methods, a lightweight loader is imported, not the ready-made API. Components become fully available only after the ymaps3.ready promise is resolved. This means that if your client code is split into 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 would be more convenient for you to describe this functionality not in a single large module, but in two different ones, for example, module-a.js and module-b.js. With the use of the above methods, the code of your modules will look something like this:

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

async function initMap() {
    // Wait until `ymaps3.ready` is resolved
    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 until `ymaps3.ready` is resolved
    await ymaps3.ready;

    const {YMap} = ymaps3;

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

initMap();

You have to wait for ymaps3.ready to be resolved in each module. This is not convenient, a simpler code would be more desirable:

// 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 (without the ymaps3.readypromise resolution)

<!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: [37.588144, 55.733842],
          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: {
        // replace YOUR_API_KEY with the real 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 ."
    }
}

Set dependencies, build a project, and run a local server:

npm install
npm run build
npm run start

Open the app

Specifics

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

In this case, fully loaded JS API modules become available and ymaps3.ready execution is guaranteed in the built project JS file, so the project code can be shorter and cleaner:

import * as ymaps3 from 'ymaps3';

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

Alert

With this connection method, the project code will not be executed until the JS API components are fully loaded.

Limitations of method 3

Let's say you want to show a loading animation until the map components are loaded. You've connected the JS API using the third method and written the following code. Unfortunately, you will not solve the task:

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

// Execution of this string will start only after ymaps3 is loaded and ymaps3.ready is resolved
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 does nothing. Although it does get executed when the component is mounted in the DOM,
    // mounting will be performed only after ymaps3 is loaded and ymaps3.ready is resolved.
    // So when mounting, the user will see the 'Loading...' status for a split second,
    // the loading animation will almost immediately be canceled by the subsequent useEffect
    if (loading) {
        return <div>Loading...</div>;
    }

    return <YMap />;
}

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

The main disadvantage of the third method of connecting Webpack is that nothing will be executed in the project modules until the JS API components are fully loaded.

Don't use it if you want to show the loading animation:

  1. Switch to Webpack connection method 1 or 2.
  2. Inside React.useEffect, wait for ymaps3.ready and only then remove the loading flag.
  3. The code that uses the Reactify module must be placed inside the MapView component so that the JS interpreter can read these strings only after ymaps3.ready is resolved:
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 />
    );
}

Switching the language

Remember the language is set when connecting the API. If you are creating international websites, regardless of the connection method you are using, make sure the language is switched correctly.

For example, to connect with Webpack using the third method, you can do it as follows:

module.exports = {
  //...
  externals: {
    ymaps3: [
      `promise new Promise((resolve) => {
          ...
          const language_region = ['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 can be used to solve specific tasks. To load them, use the ymaps3.import method.