Skip to content

Lightning Device Map Widget

Display an interactive Google Map showing all active Lightning device locations for your agency directly on your website using the Lightning Device Map Widget.

This guide covers:

  • Creating a Private Lucit Application with the Widgets capability
  • Granting the correct operator permissions
  • Creating a Public token for your website
  • Embedding the Lightning Device Map Widget on your site
  • Configuring map appearance, marker icons, and info windows
  • Customizing the container and map dimensions

This widget is primarily designed for Media Owner / Operator websites that want to display an interactive map of their screen network.


Prerequisites

Before you start, you should:

  • Have a Lucit account with access to:
  • Your Personal Profile (for creating apps)
  • At least one Agency / Operator Account (for attaching the app to your device network)
  • Your Operator account has the Lighting App and at least 1 active Lighting Device. Learn more at https://www.lucit.cc/lightning
  • Be able to:
  • Edit the HTML / template of your website (or manage embeds in your CMS)
  • Add <script> tags and small JavaScript configuration blocks to your pages
  • Have Developer Mode enabled on your profile. (Personal Profile -> Settings -> Developer Mode)
  • Have a Google Maps API key with the Maps JavaScript API enabled.
    See Get a Google Maps API Key for instructions.

1. Create a new Private Application

The widget is powered by a Lucit Application. You'll create a private app tied to your personal profile.

  1. Switch to your Personal Profile in Lucit.
  2. With Developer Mode enabled, go to Apps.
  3. Click New Application.
  4. Choose:
  5. Name — for example: My Company Website Widgets
  6. ClassPrivate
  7. Save the application.

2. Enable the Widgets capability

  1. Open your new application.
  2. Go to the Capabilities tab.
  3. Locate the Widgets capability.
  4. Turn the Widgets switch on.

3. Configure operator permissions

The widget needs permission to read device locations for your agency.

On the Permissions tab of your app, enable at least the following permissions for your agency:

  • agency.view
  • agency.viewLightningDeviceMap

These allow the widget to read the Lightning devices and their coordinates associated with your agency.


4. Create a Public token

The website widget uses a Public token to fetch content.

  1. Go to the Tokens tab of your application.
  2. Click New Token.
  3. Choose Public token.
  4. Click CREATE.
  5. Copy the generated Public Token value.

You will use this as {PUBLIC_TOKEN} in your embed code.


5. Attach the app to your Agency Account

  1. Switch from your Personal Profile to your Agency Account using the profile switcher.
  2. Go to Settings.
  3. Scroll to Apps & Data Sources.
  4. Click Add New.
  5. Find the app you just created (for example, My Company Website Widgets) and add it.

At this point, the app is:

  • Capable of serving Widgets.
  • Authorized to read device locations from your agency account.
  • Identified by a Public token that you can safely embed on your website.

6. Find your Agency LCUID

The widget requires your Agency LCUID — a unique identifier for your agency account in Lucit.

You can find it in Settings → General for your agency account. It looks like LCUID_xxxxxxxxxxxxxxxx.


7. Embed the Lightning Device Map Widget

The widget is configured using the global window.lcw queue and a script loaded from Lucit.

Minimal example

<div id="lc-map-1">Lucit Device Map</div>
<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-1",
    widget: "lightning_device_map",
    agency_id: "{YOUR_AGENCY_LCUID}",
    token: "{PUBLIC_TOKEN}",
    google_maps_api_key: "{YOUR_GOOGLE_MAPS_API_KEY}"
  });
</script>
<script
  src="https://lucit.app/embed/v1/widgets.js"
  charset="utf-8"
  async></script>

Key points:

  • The <div> id must match the id value in the configuration object.
  • Push the configuration into window.lcw before widgets.js loads.
  • The script at https://lucit.app/embed/v1/widgets.js initializes the widget system and renders all queued widgets.
  • By default the map will auto-fit to show all device locations.

8. Widget configuration reference

{
  id: string,                  // required: ID of the target <div>
  widget: "lightning_device_map", // required: must be exactly this string
  agency_id: string,           // required: your agency LCUID
  token: string,               // required: Public token from your Lucit app
  google_maps_api_key: string, // required: Google Maps JavaScript API key

  containerStyle?: { [key: string]: string }, // optional: styles applied to the outer <div>
  mapOptions?: {               // optional: map behavior and appearance
    height?: string,           // CSS height of the map, e.g. "400px" or "60vh"
    zoom?: number,             // initial zoom level — disables auto-fit when set
    center?: { lat: number, lng: number }, // initial center — disables auto-fit when set
    mapTypeId?: string,        // "roadmap" | "satellite" | "hybrid" | "terrain"
    gestureHandling?: string,  // "cooperative" | "greedy" | "none"
    infoWindow?: boolean,      // show device name on marker click (default: true)
    markerIcon?: string | object, // custom marker icon URL or icon object
    styles?: object[],         // Google Maps style array for custom colors/labels
    disableDefaultUI?: boolean,
    zoomControl?: boolean,
    streetViewControl?: boolean,
    fullscreenControl?: boolean,
    mapTypeControl?: boolean,
    // ...any other google.maps.MapOptions property
  }
}

Required fields

Field Type Description
id string The id of the DOM element where the map will render
widget string Must be "lightning_device_map"
agency_id string Your agency LCUID (e.g. LCUID_xxxxxxxxxxxxxxxx)
token string Public token from your Lucit application
google_maps_api_key string Google Maps JavaScript API key

9. Map options (mapOptions)

9.1 Map dimensions

height

Controls the CSS height of the rendered map. Accepts any valid CSS length value.

mapOptions: {
  height: "500px"
}
mapOptions: {
  height: "60vh"
}

The width of the map always fills 100% of its container. Control the container width via containerStyle.


9.2 Viewport — zoom and center

By default the map auto-fits its viewport to show all device locations. Setting either zoom or center disables auto-fit and uses the explicit values instead.

zoom

Initial zoom level. Accepts an integer from 1 (world) to 20 (building).
See Google Maps zoom levels.

mapOptions: {
  zoom: 8
}

center

Initial map center as a { lat, lng } object.
See google.maps.LatLngLiteral.

mapOptions: {
  center: { lat: 41.8781, lng: -87.6298 }
}

You can combine both to set a fixed starting view:

mapOptions: {
  zoom: 6,
  center: { lat: 44.9778, lng: -93.2650 }
}

9.3 Map type

mapTypeId

Controls the base map style. Accepted values:

Value Description
"roadmap" Standard road map (default)
"satellite" Satellite imagery
"hybrid" Satellite with road/label overlay
"terrain" Topographic relief map

See google.maps.MapTypeId.

mapOptions: {
  mapTypeId: "satellite"
}

9.4 Scroll and gesture handling

gestureHandling

Controls how the map responds to scroll and touch gestures. Default is "cooperative", which is the recommended value for embedded maps — it requires the user to hold Ctrl (desktop) or use two fingers (mobile) to zoom.
See Gesture handling.

Value Behavior
"cooperative" Ctrl+scroll to zoom on desktop; two-finger scroll on mobile (default)
"greedy" All scroll events zoom the map — can interfere with page scrolling
"none" Scroll zoom and drag pan are disabled entirely
"auto" Cooperative on mobile, greedy on desktop
mapOptions: {
  gestureHandling: "greedy"
}

9.5 Marker icon

markerIcon

Overrides the default Lucit screen marker icon. Accepts either a URL string or a Google Maps icon object.

Default icon: https://lucit.app/assets/google-maps/markers/map_icon_screen_widescreen_active.png

URL string:

mapOptions: {
  markerIcon: "https://example.com/my-custom-pin.png"
}

Icon object (controls size, anchor point, etc.):

mapOptions: {
  markerIcon: {
    url: "https://example.com/my-custom-pin.png",
    scaledSize: new google.maps.Size(40, 40),
    anchor: new google.maps.Point(20, 40)
  }
}

Note: scaledSize and anchor must use new google.maps.Size() and new google.maps.Point() respectively — plain { width, height } objects are not accepted by the Maps API for these properties. If you need to pass these values from a non-JS context (e.g. a CMS), use a URL string instead and size the icon at the image source.

See google.maps.Icon.


9.6 Info windows

infoWindow

When true (the default), clicking a marker shows a small popup with the device name. Set to false to disable popups entirely.

mapOptions: {
  infoWindow: false
}

9.7 Custom map styling

styles

Applies a custom visual style to the map — change colors, hide labels, suppress points of interest, etc.
See Styling the map and Cloud-based map styling.

You can generate style arrays using Snazzy Maps or Google's Map Style Wizard.

mapOptions: {
  styles: [
    { featureType: "poi", elementType: "all", stylers: [{ visibility: "off" }] },
    { featureType: "transit", elementType: "all", stylers: [{ visibility: "off" }] },
    { featureType: "road", elementType: "geometry", stylers: [{ color: "#f0f0f0" }] },
    { featureType: "water", elementType: "geometry", stylers: [{ color: "#c9d6e3" }] }
  ]
}

9.8 UI controls

These mirror the standard google.maps.MapOptions control flags directly.

Option Type Default Description
disableDefaultUI boolean false Hides all built-in controls at once
zoomControl boolean true Show / hide the +/− zoom buttons
streetViewControl boolean true Show / hide the Pegman (Street View) control
fullscreenControl boolean true Show / hide the fullscreen button
mapTypeControl boolean true Show / hide the Map / Satellite toggle
mapOptions: {
  disableDefaultUI: true,
  zoomControl: true   // re-enable just zoom after disabling everything else
}

10. Container styling

containerStyle accepts any CSS property in camelCase and is applied to the outer wrapper <div>.

Default container style when containerStyle is omitted or empty:

Property Default value
border "1px solid #808080"
borderRadius "5px 5px 5px 5px"
width "600px"
padding "25px 5px 25px 5px"

To suppress all default styles and control layout entirely from your own CSS, pass an empty object:

containerStyle: {}

Example — full-width, no border:

containerStyle: {
  width: "100%",
  border: "none",
  borderRadius: "0",
  padding: "0"
}

11. Full examples

Minimal — auto-fit, default styling

<div id="lc-map-minimal">Lucit Device Map</div>
<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-minimal",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY"
  });
</script>
<script src="https://lucit.app/embed/v1/widgets.js" charset="utf-8" async></script>

Full-width, taller map, no UI chrome

<div id="lc-map-fullwidth">Lucit Device Map</div>
<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-fullwidth",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY",
    containerStyle: {
      width: "100%",
      border: "none",
      borderRadius: "12px",
      padding: "0",
      overflow: "hidden"
    },
    mapOptions: {
      height: "600px",
      disableDefaultUI: true,
      zoomControl: true,
      gestureHandling: "cooperative"
    }
  });
</script>
<script src="https://lucit.app/embed/v1/widgets.js" charset="utf-8" async></script>

Fixed center and zoom — no auto-fit

<div id="lc-map-fixed">Lucit Device Map</div>
<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-fixed",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY",
    mapOptions: {
      height: "450px",
      zoom: 7,
      center: { lat: 41.8781, lng: -87.6298 },
      mapTypeId: "roadmap"
    }
  });
</script>
<script src="https://lucit.app/embed/v1/widgets.js" charset="utf-8" async></script>

Satellite view, info windows disabled

<div id="lc-map-satellite">Lucit Device Map</div>
<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-satellite",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY",
    mapOptions: {
      height: "400px",
      mapTypeId: "hybrid",
      infoWindow: false,
      streetViewControl: false,
      mapTypeControl: false
    }
  });
</script>
<script src="https://lucit.app/embed/v1/widgets.js" charset="utf-8" async></script>

Custom marker icon

<div id="lc-map-custom-icon">Lucit Device Map</div>
<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-custom-icon",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY",
    mapOptions: {
      height: "400px",
      markerIcon: "https://example.com/your-custom-pin.png"
    }
  });
</script>
<script src="https://lucit.app/embed/v1/widgets.js" charset="utf-8" async></script>

Muted color scheme — hide POIs and transit

<div id="lc-map-styled">Lucit Device Map</div>
<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-styled",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY",
    containerStyle: {
      width: "100%",
      border: "1px solid #e0e0e0",
      borderRadius: "8px",
      padding: "0",
      overflow: "hidden"
    },
    mapOptions: {
      height: "480px",
      disableDefaultUI: true,
      zoomControl: true,
      styles: [
        { featureType: "poi", elementType: "all", stylers: [{ visibility: "off" }] },
        { featureType: "transit", elementType: "all", stylers: [{ visibility: "off" }] },
        { featureType: "road", elementType: "geometry", stylers: [{ color: "#f5f5f5" }] },
        { featureType: "road", elementType: "labels.text.fill", stylers: [{ color: "#9e9e9e" }] },
        { featureType: "water", elementType: "geometry", stylers: [{ color: "#c9d6e3" }] },
        { featureType: "landscape", elementType: "geometry", stylers: [{ color: "#f9f9f9" }] }
      ]
    }
  });
</script>
<script src="https://lucit.app/embed/v1/widgets.js" charset="utf-8" async></script>

Multiple maps on the same page

You can embed multiple maps on a single page. Each must have a unique id. The Google Maps script is loaded only once regardless of how many map widgets are on the page.

<div id="lc-map-region-north">North Region</div>
<div id="lc-map-region-south">South Region</div>

<script>
  (window.lcw = window.lcw || []).push({
    id: "lc-map-region-north",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY",
    mapOptions: { height: "350px", zoom: 9, center: { lat: 45.0, lng: -93.2 } }
  });

  (window.lcw = window.lcw || []).push({
    id: "lc-map-region-south",
    widget: "lightning_device_map",
    agency_id: "LCUID_YOUR_AGENCY",
    token: "YOUR_PUBLIC_TOKEN",
    google_maps_api_key: "YOUR_GOOGLE_MAPS_API_KEY",
    mapOptions: { height: "350px", zoom: 9, center: { lat: 44.5, lng: -93.2 } }
  });
</script>
<script src="https://lucit.app/embed/v1/widgets.js" charset="utf-8" async></script>

12. Render behavior

  • The widget renders once when widgets.js loads and processes the queue.
  • After the initial render, the map is not re-fetched or re-drawn on subsequent internal ticks — the map remains stable on the page.
  • If the initial API call fails, the widget resets its state so the next internal tick will retry automatically.

13. Best practices

  • Use a dedicated Website Widgets app
    Keep widget access separate from other integrations by creating a dedicated Private application for website embeds.

  • Limit permissions
    Grant only the permissions required (view, viewDevices) for the relevant agency accounts.

  • Use gestureHandling: "cooperative" (the default)
    This prevents the map from hijacking scroll events when embedded within a scrollable page. Only change this if the map occupies a full-page or dedicated viewport.

  • Control width via containerStyle
    The map div is always width: 100% of its container. Set width on containerStyle (or let the container be sized by your page CSS by passing containerStyle: {}).

  • Use overflow: "hidden" with borderRadius
    If you apply borderRadius to containerStyle, also add overflow: "hidden" to clip the map canvas corners correctly.

  • Restrict your Google Maps API key
    In the Google Cloud Console, restrict your key to the specific domains where the widget will be embedded. Never use an unrestricted key in public embed code.

  • Test on a non-public page first
    Embed the widget on a staging or hidden URL to validate behavior before publishing.


14. Troubleshooting

Map does not appear

  • Verify that:
  • agency_id is a valid LCUID for your agency.
  • token is a valid Public token for an app with the Widgets capability.
  • google_maps_api_key is valid and has the Maps JavaScript API enabled in Google Cloud Console.
  • The <div> with the matching id exists in the page before the widget configuration is pushed.
  • Open your browser console — required-field validation errors are logged there.

Map renders but no markers appear

  • Verify that the agency has active Lightning devices with latitude/longitude data.
  • Check the browser console for API errors — a success: false response from the Lucit API will log an error.
  • Confirm the app is attached to the correct agency under Settings → Apps & Data Sources.

Map is blank / gray tiles

  • The google_maps_api_key may be invalid, expired, or restricted to a domain that does not match the current page.
  • Confirm the Maps JavaScript API is enabled for the key in Google Cloud Console.
  • Check the browser console for Google Maps API errors (e.g. ApiNotActivatedMapError, InvalidKeyMapError).

Map zooms in too close on load

  • This can happen when only one device location is returned. The widget handles this case automatically by centering on the single device at zoom level 14 rather than using fitBounds.
  • If this zoom level is too close or too far for your network, override it with an explicit zoom and center in mapOptions.

Google Maps API already loaded on the page

  • If your page already loads the Maps JavaScript API, the widget will use the existing google.maps object and will not load the API a second time.
  • If the existing Maps load uses a different API key, both will share whichever Maps instance loaded first — this is standard Google Maps behavior.

JavaScript errors in console

  • Ensure there are no syntax errors in your configuration object (missing quotes, trailing commas in older browsers, etc.).
  • Confirm widgets.js is loaded from https://lucit.app/embed/v1/widgets.js.