> For the complete documentation index, see [llms.txt](https://wpmaps-docs.mapster.me/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wpmaps-docs.mapster.me/mapster-wp-maps-v2/developer/migrating-from-v1.md).

# Migrating from V1

The developer hook system has changed significantly in V2. If you have custom code built on V1 hooks, it will need to be updated. The changes are straightforward once you understand the pattern, but they touch every hook call you've written.

> **You can always revert.** If you need more time to update your custom code, you can roll back to V1 of the plugin while you work through the migration. V1 and V2 are separate plugin versions.

## What Changed

### 1. The global object is now `mwm_v2`

V1 used a global `mwm` object. V2 uses `window.mwm_v2`.

### 2. You must wrap subscriptions in `onload()`

In V1, `mwm.add_action()` could be called anywhere on the page. In V2, `add_action` must be called inside an `mwm_v2.onload()` callback, which ensures your listeners are registered before any events fire:

```js
// V1
mwm.add_action('map_set', (map) => { ... });

// V2
window.mwm_v2.onload(() => {
  window.mwm_v2.add_action('map:loaded', ({ map }) => { ... });
});
```

### 3. Event names use colons instead of underscores

| V1 Hook                          | V2 Event            |
| -------------------------------- | ------------------- |
| `map_set`                        | `map:loaded`        |
| `data_fetched`                   | `data:fetched`      |
| `map_markers_set`                | `features:rendered` |
| `map_features_set`               | `features:rendered` |
| `map_datalayers_set`             | `features:rendered` |
| `loading_icon_done`              | `features:rendered` |
| `set_clustering`                 | `features:rendered` |
| `set_mapstyle`                   | `style:loaded`      |
| `set_terrain`                    | `terrain:set`       |
| `set_interactivity`              | `interaction:set`   |
| `map_resize_set`                 | `map:resize`        |
| `layer_feature_clicked`          | `layer:click`       |
| `layer_feature_hovered`          | `layer:mousemove`   |
| `popup_opened_from_layer_click`  | `popup:open`        |
| `popup_opened_from_layer_hover`  | `popup:open`        |
| `popup_closed_from_layer_hover`  | `popup:close`       |
| `marker_feature_clicked`         | `marker:click`      |
| `marker_feature_hovered`         | `marker:mouseenter` |
| `popup_opened_from_marker_click` | `popup:open`        |
| `popup_opened_from_marker_hover` | `popup:open`        |
| `popup_closed_from_marker_hover` | `marker:mouseout`   |

Some V1 hooks (`access_token_set`, `map_library_set`, `map_size_set`, `set_customscripts`) have no direct equivalent in V2. In most cases `map:loaded` or `features:rendered` is the right replacement.

### 4. Callback arguments have changed

In V1, each hook passed its own specific variable — `map_set` gave you `map`, `data_fetched` gave you `postResponse`, `map_markers_set` gave you `markers`, and so on.

In V2, **every event handler receives the same object**:

```js
{ map, config, features, markers, controls }
```

This means you can no longer rely on hook-specific arguments. Update your callbacks to destructure from this object instead:

```js
// V1
mwm.add_action('data_fetched', (postResponse) => {
  console.log(postResponse.locations);
});

// V2
window.mwm_v2.onload(() => {
  window.mwm_v2.add_action('data:fetched', ({ features }) => {
    console.log(features);
  });
});
```

> **Note on interaction events:** V1 passed the clicked or hovered feature directly to interaction hooks (e.g. `clickedFeature`, `hoveredFeature`). V2 does not pass event-specific data through `add_action`. To access the specific clicked feature, attach a native event listener to the `map` object using the underlying library's API (e.g. `map.on('click', ...)`). See [Examples](/mapster-wp-maps-v2/developer/examples.md) for a pattern.

### 5. Map targeting works differently

In V1, you could target a specific map by appending its ID to the hook name:

```js
mwm.add_action('map_set/mapster-7314', (map) => { ... });
```

In V2 there is no suffix syntax. Instead, check `config.id` inside the handler:

```js
window.mwm_v2.onload(() => {
  window.mwm_v2.add_action('map:loaded', ({ map, config }) => {
    if (config.id !== 7314) return;
    // only runs for map ID 7314
  });
});
```

## Custom Scripts

The Custom Scripts feature (entering a global function name in the map's settings) is **unchanged**. Your existing custom script functions will work in V2 without modification.

## Full Before/After Example

```js
// V1
let allLocations = [];
mwm.add_action('data_fetched', (postResponse) => {
  allLocations = postResponse.locations;
});
mwm.add_action('map_set', (map) => {
  map.on('load', () => {
    jQuery(document).on('click', '.map-office', function() {
      const id = parseInt(jQuery(this).attr('id').replace('map-office-', ''));
      const loc = allLocations.find(l => l.id === id);
      map.flyTo({ center: loc.data.location.coordinates, zoom: 12 });
    });
  });
});
```

```js
// V2
window.mwm_v2.onload(() => {
  window.mwm_v2.add_action('features:rendered', ({ map, features }) => {
    document.querySelectorAll('.map-office').forEach(el => {
      el.addEventListener('click', () => {
        const id = parseInt(el.id.replace('map-office-', ''));
        const feature = features.find(f => f.id === id);
        if (feature) {
          map.flyTo({ center: feature.data.location.coordinates, zoom: 12 });
        }
      });
    });
  });
});
```

Notice that in V2 you no longer need a separate `data_fetched` hook to cache features — they're available in every handler via `features`. You also no longer need to wait for `map.on('load', ...)` inside `map:loaded`, since subscribing to `features:rendered` already guarantees the map and features are fully ready.
