TypeScript types
@mapvx/web-components-ssr ships TypeScript declarations for the data exchanged between your page and <mapvx-website>: the userAction event payload, the initial-data shape and the maps SDK data models they refer to. Everything you can use from them is a type, so import it with import type. The declarations do not describe the element itself; Element typing shows what to add to your project.
import type { InitialData, UserAction } from '@mapvx/web-components-ssr'Importing types
The declarations are part of the npm package; there is no separate @types package. Install the exact version you tested (see npm):
npm install --save-exact @mapvx/web-components-ssr@1.6.1- Entry: the package’s
typesfield points todist/types/public-api.d.ts. It is a single file with no imports, so TypeScript needs no other package to resolve it. - Import path: always the package root,
'@mapvx/web-components-ssr', withimport type. It resolves withmoduleResolutionset tobundler,node16ornodenext, and withnode10, which TypeScript 6.0 deprecates (there it requires"ignoreDeprecations": "6.0"). - DOM library: the file references a DOM type (
GeolocationPosition). In atsconfig.jsonwithout theDOMlibrary, such as one for server-only code, TypeScript reportsCannot find name 'GeolocationPosition'inside the file. Add"DOM"tolibor enableskipLibCheck. - Plain JavaScript: to get the types in an editor that checks JavaScript, reference them from JSDoc. JSDoc imports are comments, so they never load the bundle.
/** @typedef {import('@mapvx/web-components-ssr').UserAction} UserAction */What the file exports:
| Group | Exports | Details |
|---|---|---|
| Event payloads | UserAction, SelectFilterAction, ShowPlaceAction, SelectDestinationAction, SearchAction, ReturnToHomeAction | UserAction |
| Initial data | InitialData, PlaceResponse, InstitutionResponse | InitialData |
| Component models | AnimationState, CategoryFiltersData, CurrentLocation, FormattedSchedule, RoutePoint, SearchData | Other models |
| Maps SDK data types | MVXPlace, Institution, Banner, MVXRoute, MVXRouteLeg, MVXRouteStep, MVXTransport, MVXLocation, CityFilterOption, OpeningHours, OpeningTime, DaysOfTheWeek, LatLng, InnerFloor, Property | Maps SDK types |
| Declared values, not importable | EMPTY_SEARCH_DATA, FALLBACK_IMAGE_URL, MEASUREMENT_ID, extractBannerColor, getSchedule, isCurrentLocation, isDealOrEventBanner, isPlace, prepareAllSchedules, toCurrentLocation | Types only |
Types only
The package’s runtime entry (main) is dist/browser/main.js, the component bundle. Loading it registers <mapvx-website>; it is not a library. Its only exports are internal names that its own lazy chunks import, so none of the names in the declaration file can be imported at runtime.
The declaration file still declares some runtime values: the constants and functions in the last row of the table above, the AnimationState enum and the SDK classes (MVXPlace, Institution, Banner, MVXRoute, MVXRouteLeg, MVXRouteStep, MVXTransport). TypeScript accepts a value import of any of them, but the import fails afterwards:
- A bundler stops the build because
main.jshas no such export. esbuild, for example, reportsNo matching export in "node_modules/@mapvx/web-components-ssr/dist/browser/main.js" for import "isPlace". - A browser that loads the module natively throws a
SyntaxErrorbecause the module does not provide the requested export. - Any import of
'@mapvx/web-components-ssr'that remains in the compiled output loadsmain.js, which registers the element. In server code it throwsReferenceError: HTMLElement is not defined(see Troubleshooting).
Names imported with import type can only be used as types. new MVXPlace(...), value instanceof MVXPlace and AnimationState.Playing do not compile (TS1361: ... cannot be used as a value because it was imported using 'import type'). What the component gives you, such as event.detail, is plain data, so no constructor is needed.
Write import type, not an inline type modifier:
// Do: removed completely from the compiled output
import type { UserAction } from '@mapvx/web-components-ssr'
// Avoid: with verbatimModuleSyntax this compiles to
// import {} from '@mapvx/web-components-ssr'
import { type UserAction } from '@mapvx/web-components-ssr'With verbatimModuleSyntax enabled, when every specifier of an import has an inline type modifier, TypeScript keeps it as an empty import, and an empty import still loads the bundle.
UserAction
The detail of every userAction event is a UserAction, a union discriminated by type. The declarations:
export interface FilterSelection {
/** First selected category, or 'All' when none are on. Kept for compatibility. */
filter: string
/** The whole selection. Empty means no category filter. */
filters: string[]
}
export interface SelectFilterAction {
type: 'select-filter'
data: FilterSelection
}
export interface ShowPlaceAction {
type: 'show-place'
data: { placeId: string; alias: string | undefined }
}
export interface SelectDestinationAction {
type: 'select-destination'
data: { destinationId: string; alias: string | undefined }
}
export interface SearchAction {
type: 'search'
data: { searchTerm: string }
}
export interface ReturnToHomeAction {
type: 'return-to-home'
data: { filter: string }
}
export type UserAction =
| SelectFilterAction
| ShowPlaceAction
| SelectDestinationAction
| SearchAction
| ReturnToHomeActiontype | data | Emitted when |
|---|---|---|
select-filter | { filter, filters } | The visitor turns a category on or off. filters is the whole selection. |
show-place | { placeId, alias } | A place profile opens: card or map marker click, tenant deep link, or Back from the route screen. |
select-destination | { destinationId, alias } | The visitor clicks Pedestrian-friendly directions on a profile. |
search | { searchTerm } | A search runs for a new term. |
return-to-home | { filter } | The component goes back to its home screen from a profile or from search. filter is the selected category name, or an empty string when there are no categories. |
- Ids:
placeIdanddestinationIdare the place’s MapVX id, which ismapvxIdonMVXPlaceandidinPlaceResponse.aliasis the place’sclientId: itsaliasin MapVX data, or itsexternalIdwhen it has no alias, andundefinedwhen it has neither. Thealiaskey is always present. - cardSelected: this legacy event has no exported type. Its
detailis the place’s MapVX id, so type it asCustomEvent<string>(see cardSelected). - Details: when each action fires, and how often, is described in Action types.
A typed listener, without any element typing:
import type { UserAction } from '@mapvx/web-components-ssr'
const el = document.querySelector('mapvx-website')
el?.addEventListener('userAction', (event) => {
const action = (event as CustomEvent<UserAction>).detail
switch (action.type) {
case 'select-filter':
case 'return-to-home':
console.log('Category:', action.data.filter)
break
case 'show-place':
console.log('Place:', action.data.placeId, action.data.alias)
break
case 'select-destination':
console.log('Directions to:', action.data.destinationId)
break
case 'search':
console.log('Search:', action.data.searchTerm)
break
default: {
// Compile error here if a new action type is not handled above
const unhandled: never = action
console.warn('Unhandled action', unhandled)
}
}
})Each case narrows action.data to that member’s shape. The never assignment turns an unhandled member into a compile error, for example after an upgrade that adds one. Attach the listener to the element itself with the exact name userAction, because the event does not bubble (see Listening to events).
InitialData
The shape behind the initial-data attribute:
export interface InitialData {
availablePlaces: PlaceResponse[] | null
institutions: InstitutionResponse[] | null
}- A JSON string: the attribute and the
initialDataproperty take this object serialized as JSON.prepareInitialDatafrom@mapvx/web-components-ssr-helpersalready resolves to that string, so pass it unchanged. An object set as the property throws aTypeErrorinside the component. - Raw API shapes:
PlaceResponseandInstitutionResponseare the objects the MapVX API returns, with the id inid. The component converts them toMVXPlaceandInstitution, where the id ismapvxId. - Only these two keys: other keys are ignored, and a key set to
nullis skipped. The helper sets a key tonullwhen its request gets an HTTP error status. - Where the type lives: the helper package does not export
InitialData; import it from@mapvx/web-components-ssr.
Server code that checks the data before rendering:
import type { InitialData } from '@mapvx/web-components-ssr'
import { prepareInitialData } from '@mapvx/web-components-ssr-helpers'
const initialData = await prepareInitialData('YOUR_API_KEY') // JSON string
const { availablePlaces, institutions } = JSON.parse(initialData) as InitialData
console.log(`${availablePlaces?.length ?? 0} places, ${institutions?.length ?? 0} institutions`)
// Pass initialData (the string) to the initial-data attribute, not the parsed objectError handling and timeouts for the helper are covered in prepareInitialData.
In 1.6.1, using initial-data together with api-key lists every place twice. See the known issue.
Other models
The remaining exports describe the component’s internal state. None of them appears in the element’s attributes or events.
| Type | Shape | Describes |
|---|---|---|
AnimationState | enum: NotStarted = 0, Playing = 1, Paused = 2, Finished = 3, Stepping = 4 | The route animation. Stepping, added in 1.4.0 as the last member, advances the route one step per visitor action instead of playing on its own. |
CategoryFiltersData | { name: string; placeData: string[] } | A category on offer: its label and the MapVX ids of its places (empty for All). Also the payload of categoriesChange. |
FormattedSchedule | { day: string; schedule: string[] } | One row of the weekly hours. day can be a range such as Monday - Friday. |
SearchData | { term: string; results: MVXPlace[] } | A search term and its results. |
CurrentLocation | { type: 'current-location'; title: 'Current location'; position: LatLng } | The visitor’s position, used as a point of a route. |
RoutePoint | MVXPlace or CurrentLocation | A route origin or destination. |
These declared values exist inside the bundle, but main.js does not export them (see Types only):
| Name | What it is |
|---|---|
EMPTY_SEARCH_DATA | A SearchData with an empty term and no results. |
FALLBACK_IMAGE_URL | The placeholder image that cards and place profiles show when a place has neither photos nor a logo. |
MEASUREMENT_ID | The measurement ID of MapVX’s Google Analytics property, which the component reports to (see Google Analytics). |
toCurrentLocation(position) | Converts a GeolocationPosition into a CurrentLocation. |
isCurrentLocation(point) | Type guard: the point is a CurrentLocation. |
isPlace(point) | Type guard: the point is an MVXPlace (it has a mapvxId). |
isDealOrEventBanner(banner) | Whether a banner is tagged deals or events. |
extractBannerColor(description) | The color declared in the first inline style attribute of a banner description, or #EE5845. |
getSchedule(openingHours) | Today’s hours as an HTML string: the previous day’s hours while an overnight interval is still open, or text such as Open on Monday when the place does not open today. Computed with the viewer’s clock and time zone. |
prepareAllSchedules(openingHours) | The weekly hours as FormattedSchedule[], starting on Monday. Consecutive days with the same hours share one row (the last row, which ends on Sunday, joins the first one when their hours match), and days without hours show Closed. The hours are HTML strings. |
Maps SDK types
The component is built on @mapvx/web-js, the MapVX maps SDK, which is bundled inside main.js (1.6.1 bundles SDK version 3.1.0). The package re-exports the SDK’s data types, and their declarations are copied into public-api.d.ts, so you do not need to install @mapvx/web-js to use them.
| Type | Kind | Description |
|---|---|---|
MVXPlace | class | A place: mapvxId, clientId, title, position, category, localizedCategory, address, openingHours, images, logo, banners, floors, parentId and parentInstitutionId. |
Institution | class | A group of places, such as a shopping mall: mapvxId, title, country, optional city, category, properties. |
Banner | class | A place banner: url, tags, urlAction, sameLink and an optional description that can contain HTML. |
MVXRoute | class | A route made of legs, with steps, duration (seconds) and length (meters) getters. |
MVXRouteLeg | class | The part of a route between two points: distance, duration, start and end locations, steps. |
MVXRouteStep | class | One navigation step: instructions, distance, duration, locations, floors and elevatorStep. |
MVXTransport | class | Transport schedules and real-time routes. |
MVXLocation | interface | A LatLng with optional floorId and placeId. |
CityFilterOption | union | An entry of the city filter: an institution option (type: 'institution') or a parent-place option (type: 'parent-place', with parentPlaceId), each with filterId, name and institutionId. |
OpeningHours | type | The opening intervals of each day, keyed by DaysOfTheWeek. |
OpeningTime | interface | One interval: open and close as 24-hour times such as 09:00. |
DaysOfTheWeek | type | 'mo', 'tu', 'we', 'th', 'fr', 'sa' or 'su'. |
LatLng | interface | { lat: number; lng: number } |
InnerFloor | interface | A floor of a building: key, index, level, name, defaultFloor. |
Property | interface | A property of a place or institution: title, subtitle and the optional icon, url and type. The category tabs come from the institution’s properties. |
PlaceResponse | interface | A place as the MapVX API returns it (id, title, lat, lng and more). Used in InitialData. |
InstitutionResponse | interface | An institution as the MapVX API returns it. Used in InitialData. |
MVXPlace, Institution, Banner and the route and transport types are declared as classes. Use them as types only: their constructors and static methods are not available to your code (Types only).
Element typing
The package does not type the <mapvx-website> element: it has no HTMLElementTagNameMap entry, no JSX IntrinsicElements entry and no event map. Without them, document.querySelector('mapvx-website') is typed as a plain Element | null, and TSX reports Property 'mapvx-website' does not exist on type 'JSX.IntrinsicElements'. Add a declaration file to your project. The declarations below were type-checked with TypeScript 5.9 and 6.0.
- Any
.d.tsfile that theincludeof yourtsconfig.jsoncovers works. Do not give it the same name as a.tsor.tsxfile in the same folder (for examplemapvx-website.d.tsnext tomapvx-website.tsx): TypeScript then skips the.d.tsfile without any warning. - The DOM and React declarations can live in the same file.
DOM and events
This declaration types querySelector, createElement, the element’s properties and its two events:
// types/mapvx-website.d.ts
import type { UserAction } from '@mapvx/web-components-ssr'
export interface MapvxWebsiteEventMap extends HTMLElementEventMap {
userAction: CustomEvent<UserAction>
cardSelected: CustomEvent<string>
}
export interface MapvxWebsiteElement extends HTMLElement {
apiKey: string
institutionId: string
initialData: string // a JSON string, never an object
showCategoryFilters: boolean
showCityFilter: boolean
defaultToMap: boolean
inheritFontFamily: boolean
hideDeals: boolean
showDealLabels: boolean
cardWidth: number | undefined
rowsPerPage: number
addEventListener<K extends keyof MapvxWebsiteEventMap>(
type: K,
listener: (this: MapvxWebsiteElement, event: MapvxWebsiteEventMap[K]) => void,
options?: boolean | AddEventListenerOptions,
): void
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void
removeEventListener<K extends keyof MapvxWebsiteEventMap>(
type: K,
listener: (this: MapvxWebsiteElement, event: MapvxWebsiteEventMap[K]) => void,
options?: boolean | EventListenerOptions,
): void
removeEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions,
): void
}
declare global {
interface HTMLElementTagNameMap {
'mapvx-website': MapvxWebsiteElement
}
}With it, no cast is needed:
const el = document.querySelector('mapvx-website') // MapvxWebsiteElement | null
el?.addEventListener('userAction', (event) => {
// event: CustomEvent<UserAction>
if (event.detail.type === 'search') console.log('Search:', event.detail.data.searchTerm)
})
el?.addEventListener('cardSelected', (event) => console.log('Card:', event.detail)) // string
const created = document.createElement('mapvx-website')
created.rowsPerPage = 3- The property types are the values the component uses after parsing its inputs. How each property accepts and returns values is described in JavaScript properties.
- A misspelled event name, such as
'useraction', matches no entry of the event map. The listener then receives a plainEvent, so readingevent.detailis reported as an error.
Listener helper
When the element reference is not typed, for example a framework ref typed as HTMLElement, this helper gives you a typed UserAction and returns a function that removes the listener:
// on-user-action.ts
import type { UserAction } from '@mapvx/web-components-ssr'
export function onUserAction(element: Element, handler: (action: UserAction) => void): () => void {
const listener = (event: Event) => handler((event as CustomEvent<UserAction>).detail)
element.addEventListener('userAction', listener)
return () => element.removeEventListener('userAction', listener)
}import { onUserAction } from './on-user-action'
const el = document.querySelector('mapvx-website')
if (el) {
const stop = onUserAction(el, (action) => {
if (action.type === 'show-place') console.log('Place:', action.data.placeId)
})
// Call stop() to remove the listener, for example when your view unmounts
}React
TSX needs a JSX declaration for the tag. Both versions use the declare module 'react' form; the file must have a top-level import, as these do, for TypeScript to treat it as an augmentation of the react module.
React 19
// types/mapvx-website-react.d.ts
import type { DetailedHTMLProps, HTMLAttributes } from 'react'
import type { UserAction } from '@mapvx/web-components-ssr'
// React 19 renders {true} as "" and drops {false}: accept strings only
type BooleanAttribute = 'true' | 'false'
interface MapvxWebsiteProps extends DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> {
'api-key'?: string
'institution-id'?: string
'initial-data'?: string
'show-category-filters'?: BooleanAttribute
'show-city-filter'?: BooleanAttribute
'show-view-toggle'?: BooleanAttribute
'show-search-bar'?: BooleanAttribute
'show-drop-pin'?: BooleanAttribute
'selected-categories'?: string
'search-term'?: string
'full-width-map'?: BooleanAttribute
'hide-unselected-markers'?: BooleanAttribute
'default-to-map'?: BooleanAttribute
'inherit-font-family'?: BooleanAttribute
'hide-deals'?: BooleanAttribute
'show-deal-labels'?: BooleanAttribute
'card-width'?: number | string
'rows-per-page'?: number | string
'zoom'?: number | string
'center'?: string
// React 19 listens to the event named after "on", with its exact case
onuserAction?: (event: CustomEvent<UserAction>) => void
oncardSelected?: (event: CustomEvent<string>) => void
}
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
'mapvx-website': MapvxWebsiteProps
}
}
}import type { UserAction } from '@mapvx/web-components-ssr'
export function Directory({ initialData }: { initialData?: string }) {
const handleAction = (event: CustomEvent<UserAction>) => {
const action = event.detail
if (action.type === 'show-place') console.log('Place:', action.data.placeId)
}
return (
<mapvx-website
api-key="YOUR_API_KEY"
institution-id="YOUR_INSTITUTION_ID"
initial-data={initialData}
default-to-map="true"
show-city-filter="false"
rows-per-page={3}
onuserAction={handleAction}
oncardSelected={(event) => console.log('Card:', event.detail)}
/>
)
}- React 19 renders
{true}as an empty attribute, which the component reads as false, and omits{false}, so on the first render the default applies (true forshow-category-filtersandshow-city-filter). Typing the booleans as'true' | 'false'turns both mistakes into compile errors. - The event props are
onuserActionandoncardSelected:onfollowed by the event name with its exact case.onUserActionnever fires, and this declaration rejects it. @types/react19 has no globalJSXnamespace, so adeclare global { namespace JSX { ... } }block has no effect there.- This form was checked with
@types/react19.
Load the bundle in the browser only; see React and Next.js.
The JSX declaration applies to React TSX. With other frameworks, the DOM declaration and the listener helper type the element in your script code. Loading the bundle and configuring templates are covered in Vue, Angular and Other frameworks.