Integration with Vue

Quick start

Warning

Supported Vue version: at least 3

Connecting via top-level-await

<!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.ts"></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
import {createApp} from 'vue';
import App from './App.vue';

createApp(App).mount('#app');
<script lang="ts" setup>
  import {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} from './lib/ymaps';
  import type {YMapLocationRequest} from 'ymaps3';

  const LOCATION: YMapLocationRequest = {
    center: [25.229762, 55.289311],
    zoom: 9
  };
</script>

<template>
  <div style="width: 600px; height: 400px">
    <YMap :location="LOCATION">
      <YMapDefaultSchemeLayer />
      <YMapDefaultFeaturesLayer />

      <YMapMarker :coordinates="[25.229762, 55.289311]" :draggable="true">
        <section>
          <h1>You can drag this header</h1>
        </section>
      </YMapMarker>
    </YMap>
  </div>
</template>
import * as Vue from 'vue';

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

export const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
export const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = vuefy.module(ymaps3);
{
  "compilerOptions": {
    "target": "es2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "typeRoots": ["./node_modules/@types", "./node_modules/@yandex/ymaps3-types"],
    "paths": {
      "ymaps3": ["./node_modules/@yandex/ymaps3-types"]
    }
  }
}
{
  "type": "module",
  "scripts": {
    "dev": "vite"
  },
  "devDependencies": {
    "@yandex/ymaps3-types": "^0.0.17",
    "vue": "^3.4.21",
    "@vitejs/plugin-vue": "^5.0.4",
    "typescript": "^4.9.5",
    "vite": "^5.2.8"
  }
}
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      vue: 'vue/dist/vue.esm-bundler.js'
    }
  }
});

Install the dependencies and start the local server:

npm install
npm run dev

Open application

Specificities

  1. 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.ts"></script>
    
  2. In package.json adding a dev-dependency on the package @yandex/ymaps3-types.

    It is recommended to install the latest version:

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

  3. In tsconfig.json we set compilerOptions.typeRoots with a list of paths to file types. Adding the path to the package @yandex/ymaps3-types there, so that the namespace ymaps3 with types appears in the global scope.

    Note

    The namespace ymaps3 contains all the class types that the JS API provides, but they are not available in the runtime until the resolving ymaps3.ready.

  4. In tsconfig.json we set compilerOptions.paths, in which we inform the ts compiler that when importing the package ymaps3, its content should be searched for in the specified path. Thanks to this, you can import types in project files as if they are not in the @yandex/ymaps3-types, but in the ymaps3 package:

    import type {YMapLocationRequest} from 'ymaps3';
    

    All types must be imported from the root.

    The internal structure is not guaranteed and may change over time.

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

  6. In vite.config.ts we set resolve.alias to use the "full" Vue build (vue/dist/vue.esm-bundler.js). This is necessary for the correct operation of the ymaps3 Vue components.

  7. For each object in the JS API, there is a Vue analog. To use the Vue API version, connect the @yandex/ymaps3-vuefy module. In the lib/ymaps.ts file, we wait for the JS API and vuefy module to be fully loaded, after which we export the necessary map components for use in other parts of the project:

    import * as Vue from 'vue';
    
    const [ymaps3Vue] = await Promise.all([ymaps3.import('@yandex/ymaps3-vuefy'), ymaps3.ready]);
    
    export const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
    export const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = vuefy.module(ymaps3);
    
  8. Using top-level-await in lib/ymaps.ts guarantees the execution of ymaps3.ready and ymaps3.import('@yandex/ymaps3-vuefy') before importing map components, which allows you to synchronously use any JS API objects as Vue components:

    <YMap :location="LOCATION">
      <YMapDefaultSchemeLayer />
      <YMapDefaultFeaturesLayer />
      ...
    </YMap>
    

Specifying input props for your own classes

Vue components require explicit declaration of props so that Vue knows which of them should be treated as additional attributes.

For basic objects from the ymaps3 namespace, the input parameters are defined by default, and no additional actions are required.

If you are a developer who creates your own classes (for example, within a separate package), then you can determine which input parameters will be in the component. The props definition supports the Vue format. For example, the custom class YMapSomeClass:

type YMapSomeClassProps = {
  id: string;
  counter?: number;
};
export class YMapSomeClass extends ymaps3.YMapComplexEntity<YMapSomeClassProps> {
  static [ymaps3Vue.vuefy.optionsKey] = {props: ['id', 'counter']};
  //...
}

In addition to the array of strings, we can also use the object syntax:

export class YMapSomeClass extends ymaps3.YMapComplexEntity<YMapSomeClassProps> {
  static [ymaps3Vue.vuefy.optionsKey] = {
    props: {
      id: String,
      counter: Number
    }
  };
  //...
}

or with validation of input parameters:

export class YMapSomeClass extends ymaps3.YMapComplexEntity<YMapSomeClassProps> {
  static [ymaps3Vue.vuefy.optionsKey] = {
    props: {
      id: {type: String, required: true},
      counter: {type: Number, required: false}
    }
  };
  //...
}

Specifying input props when using third-party packages

If the developers of third-party packages haven't defined input parameters for their classes, you should specify them when calling vuefy yourself by passing them in as the second argument:

const ymaps3Vue = await ymaps3.import('@yandex/ymaps3-vuefy');
const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
const {YMapSomeClass as YMapSomeClassV} = vuefy.module(
  {YMapSomeClass},
  {
    YMapSomeClass: ['id', 'counter'] // props for the YMapSomeClass
  }
);

Similarly, the object syntax and validations shown above are supported.

Custom implementations of objects ymaps3.YMapEntity for Vue

When the standard conversion is not sufficient, use the overrideKey key to specify your own implementation of vuefy:

type YMapSomeClassProps = {
  id: string;
  counter?: number;
};
/* object ymaps3.YMapEntity */
export class YMapSomeClass extends ymaps3.YMapComplexEntity<YMapSomeClassProps> {
  static [ymaps3Vue.vuefy.overrideKey] = YMapSomeClassVuefyOverride;
  //...
}

YMapSomeClassVuefyOverride is the method that should return the Vue component. As parameters, it gets a base class, declared props, and an object with Vue and vuefy if their methods are required. In the example below, a wrapper YMapSomeClassV is created with additional logic around the component obtained by the basic vuefy method:

export const YMapSomeClassVuefyOverride: CustomVuefyFn<YMapSomeClass> = (
  YMapSomeClassI, // basic YMapSomeClass
  props, // declared props
  {vuefy, Vue}
) => {
  // Standard vuefy method
  const YMapSomeClassVuefied = vuefy.entity(YMapSomeClassI, props);
  const YMapSomeClassV = Vue.defineComponent({
    props,
    name: 'YMapSomeClassV',
    components: {YMapSomeClassVuefied},
    setup() {
      // additional logic of the user implementation
    },
    template: `<YMapSomeClassVuefied v-bind="$props" ... />`
  });
  return YMapSomeClassV;
};

the resulting component can be used in the application:

const ymaps3Vue = await ymaps3.import('@yandex/ymaps3-vuefy');
const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
const YMapSomeClassV = vuefy.entity(YMapSomeClass);
const app = createApp({
  components: {YMapSomeClassV},
  template: `<YMapSomeClassV id="some_id" />`
});
app.mount('#app');