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
- The JS API is distributed exclusively by the link that needs to be connected in the header of the document.
- API components are always loaded asynchronously.
- Components exist only in the global scope in the variable
ymaps3
. - 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
|
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 via top-level-await (recommended)
<!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 type="module" src="index.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps.js'
const map = new YMap(
document.getElementById('app'),
{
location: {
center: [25.229762, 55.289311],
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 ."
}
}
Install the dependencies and start the local server:
npm install
npm run start
Open application
Specificities
-
In the script tag that loads the compiled project js, specify the attribute
type="module"
to activate support for ECMAScript Modules (ESM) and top-level-await:<script type="module" src="index.js"></script>
ESM support when using bundles
If a bundler is used to build the project, for example Webpack, then in the
package.json
file needs to add"type": "module"
-
In the file
lib/ymaps.js
we are waiting for the JS API to be fully loaded, after which we export the necessary map components for use in other parts of the project:await ymaps3.ready; export const {YMap, YMapDefaultSchemeLayer} = ymaps3;
-
Using top-level-await in
lib/ymaps.js
guarantees the execution ofymaps3.ready
before importing map components, so the project code can be written shorter and neater (the asynchronous initMap function is no longer needed):import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps.js' const map = new YMap({...});
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
-
On the
HTML
page, we are still connecting theJS 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. -
In
webpack.config.js
declaring an external variableymaps3
:module.exports = { // ... externals: { ymaps3: 'ymaps3' } };
Thanks to this, the project code makes it possible to import
ymaps3
as if theymaps3
code is delivered not through a global variable, but through annpm
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:
-
It is no longer necessary to connect the tag
<script>
in the header of theHTML
page. This is done bywebpack
itself. -
In
webpack.config.js
the external variableymaps3
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 module-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:
- Switch to webpack connection #1 or #2.
- Inside
React.useEffect
, you need to wait forymaps3.ready
and only then uncheckloading
. - The code using the
reactify
module must be placed inside theMapView
component so that theJS
interpreter can "read" these lines only after theymaps3.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.