npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bradford-tech/expo-carplay

v0.2.2

Published

A React Native Expo module for integrating Apple CarPlay navigation templates

Readme

@bradford-tech/expo-carplay

Expo module for integrating Apple CarPlay into React Native apps. Provides typed APIs for map templates, turn-by-turn navigation, maneuvers, travel estimates, and more — with an Expo config plugin for automatic iOS setup.

Note: This module is in active development. The core navigation flow is working (map, routes, maneuvers, travel estimates). Additional template types are planned. See the API Surface tables below for current status.

Install

npm install @bradford-tech/expo-carplay

Requires Node 20+, Expo SDK 55+, and iOS 15.0+.

Quick Start

import {
  addBarButtonPressedListener,
  addConnectListener,
  addTripStartedListener,
  createMapTemplate,
  createSearchTemplate,
  pushTemplate,
  setCarPlayRoute,
  setRootTemplate,
  showTripPreviews,
  startFollowingUser,
  startNavigation,
  updateManeuvers,
  updateTravelEstimates,
  useCarPlay,
} from "@bradford-tech/expo-carplay";
import { useEffect } from "react";

const trip = {
  origin: { latitude: 37.335, longitude: -122.009 },
  destination: { latitude: 37.345, longitude: -122.002 },
  routeChoices: [
    {
      summaryVariants: ["Via Infinite Loop", "Fastest route"],
      additionalInformationVariants: ["0.5 mi — 2 min"],
    },
  ],
};

export default function App() {
  const { connected } = useCarPlay();

  useEffect(() => {
    const connectSub = addConnectListener(async () => {
      const templateId = await createMapTemplate({
        leadingNavigationBarButtons: [
          { id: "search", systemImage: "magnifyingglass" },
        ],
      });
      await setRootTemplate(templateId);
    });

    const barButtonSub = addBarButtonPressedListener(async ({ id }) => {
      if (id === "search") {
        const searchId = await createSearchTemplate();
        await pushTemplate(searchId);
      }
    });

    // When user taps "Go" on a trip preview
    const tripStartedSub = addTripStartedListener(async () => {
      await startFollowingUser();
      await startNavigation(trip);
      await updateManeuvers([
        {
          instructionVariants: ["Head northeast on Infinite Loop"],
          symbolImage: { systemName: "arrow.up" },
          distanceRemaining: 800,
          timeRemaining: 120,
        },
      ]);
      await updateTravelEstimates({ distanceRemaining: 800, timeRemaining: 120 }, 0);
    });

    return () => {
      connectSub.remove();
      barButtonSub.remove();
      tripStartedSub.remove();
    };
  }, []);

  const handleStartNavigation = async () => {
    if (!connected) return;

    // 1. Draw the route on the map
    await setCarPlayRoute([
      {
        coordinates: [
          { latitude: 37.335, longitude: -122.009 },
          { latitude: 37.339, longitude: -122.005 },
        ],
        color: "systemTeal",
      },
    ]);

    // 2. Show trip preview — user taps "Go" to start navigation
    await showTripPreviews([trip]);
  };
}

Expo Config Plugin

The module includes an Expo config plugin that automatically configures your iOS project:

  • Adds the CarPlay scene manifest to Info.plist
  • Adds CarPlay entitlements
  • Bridges the phone scene delegate for multi-scene support

No manual Xcode configuration needed — just install and run npx expo prebuild.

API Surface

The module wraps Apple's CarPlay framework (iOS 12.0 – 26.4) for use from JavaScript/TypeScript. The tables below show every feature area and its implementation status.

Status: ● Implemented · ○ Not yet implemented

Implementation Summary

| Category | Status | Notes | | --- | --- | --- | | Expo Config Plugin | ● | Info.plist scene manifest, CarPlay entitlements, phone scene delegate bridge | | Privacy Manifest | ● | PrivacyInfo.xcprivacy declares precise location for app functionality | | Web stub | ● | All methods resolve to safe defaults with a one-time warning so web bundles can import the package | | Scene Lifecycle | ● | Connect/disconnect events, interface controller & window references | | Interface Controller | Partial | setRootTemplate, pushTemplate, popTemplate; present/dismiss not yet exposed | | Map Template | ● | Creation with button config, map delegate, MKMapView with location tracking, route polylines | | Navigation Session | ● | Start/stop, trip previews, maneuver updates, travel estimate updates | | Maneuvers | Partial | instructionVariants, symbolImage (SF Symbols), initialTravelEstimates | | Trip & Route Choice | ● | Origin/destination, route choice summary variants | | Travel Estimates | ● | Distance remaining, time remaining | | List Template | ○ | Not started | | Grid Template | ○ | Not started | | Search Template | ● | Create, search text events, result selection, request ID pattern | | Tab Bar Template | ○ | Not started | | Information Template | ○ | Not started | | Voice Control Template | ○ | Not started | | Alerts & Action Sheets | ○ | Not started | | Session Configuration | ○ | Not started | | Contact Template | ○ | Not started | | Point of Interest | ○ | Not started | | Now Playing | ○ | Not started | | Dashboard & Instrument Cluster | ○ | Not started | | Multitouch (iOS 26+) | ○ | Not started |

React Hooks

| Hook | Description | Status | | --- | --- | --- | | useCarPlay() | Returns { connected: boolean } — reactive CarPlay connection state | ● |

Scene Lifecycle & Interface Controller

| API | Kind | Status | | --- | --- | --- | | addConnectListener(listener) | Native→JS | ● | | addDisconnectListener(listener) | Native→JS | ● | | isConnected() | JS | ● | | setRootTemplate(templateId) | JS→Native | ● | | pushTemplate(templateId) | JS→Native | ● | | popTemplate() | JS→Native | ● | | popToRootTemplate(animated:) | JS→Native | ○ | | presentTemplate(_:animated:) | JS→Native | ○ | | dismissTemplate(animated:) | JS→Native | ○ | | Interface controller delegate events | Native→JS | ○ | | Content style observation | Native→JS | ○ |

Map Template

| API | Kind | Status | | --- | --- | --- | | createMapTemplate(config?) | JS→Native | ● | | startFollowingUser() | JS→Native | ● | | stopFollowingUser() | JS→Native | ● | | setCarPlayRoute(segments) | JS→Native | ● | | clearCarPlayRoute() | JS→Native | ● | | Map delegate (panning events) | Native→JS | ○ | | Map buttons (MapButtonConfig[]) | Config | ● | | Navigation bar buttons (BarButtonConfig[]) | Config | ● | | updateMapTemplateButtons(config) | JS→Native | ● | | addBarButtonPressedListener(listener) | Native→JS | ● | | addMapButtonPressedListener(listener) | Native→JS | ● | | showPanningInterface(animated:) | JS→Native | ○ | | dismissPanningInterface(animated:) | JS→Native | ○ |

Navigation Session

| API | Kind | Status | | --- | --- | --- | | startNavigation(trip) | JS→Native | ● | | stopNavigation() | JS→Native | ● | | updateManeuvers(maneuvers) | JS→Native | ● | | updateTravelEstimates(estimates, maneuverIndex?) | JS→Native | ● | | getActiveSessionId() | JS | ● | | showTripPreviews(trips) | JS→Native | ● | | hideTripPreviews() | JS→Native | ● | | addTripPreviewSelectedListener(listener) | Native→JS | ● | | addTripStartedListener(listener) | Native→JS | ● | | showRouteChoicesPreview(for:textConfiguration:) | JS→Native | ○ | | cancelTrip() | JS→Native | ○ | | pauseTrip(for:description:) | JS→Native | ○ | | resumeTrip(updatedRouteInformation:) | JS→Native | ○ | | Map template navigation delegate events | Native→JS | ○ |

Trip & Route Choice

| API | Kind | Status | | --- | --- | --- | | CPTrip (origin, destination, routeChoices) | Config | ● | | CPRouteChoice (summaryVariants, additionalInformationVariants) | Config | ● | | CPTravelEstimates (distanceRemaining, timeRemaining) | Config | ● | | CPTripPreviewTextConfiguration | Config | ○ | | CPRouteInformation (iOS 17.4+) | Config | ○ | | CPRouteSegment (iOS 26.4+) | Config | ○ | | CPNavigationWaypoint (iOS 26.4+) | Config | ○ |

Maneuvers

| API | Kind | Status | | --- | --- | --- | | instructionVariants | Config | ● | | symbolImage (SF Symbols) | Config | ● | | initialTravelEstimates | Config | ● | | attributedInstructionVariants | Config | ○ | | symbolSet (CPImageSet) | Config | ○ | | junctionImage | Config | ○ | | dashboardSymbolImage / dashboardInstructionVariants | Config | ○ | | notificationSymbolImage / notificationInstructionVariants | Config | ○ | | maneuverType (54 cases) | Config | ○ | | maneuverState | Config | ○ | | junctionType / trafficSide | Config | ○ | | linkedLaneGuidance | Config | ○ | | Lane guidance (CPLaneGuidance, CPLane) | Config | ○ |

Navigation Alerts

| API | Kind | Status | | --- | --- | --- | | CPNavigationAlert (title, subtitle, image, actions, duration) | Config | ○ | | present(navigationAlert:animated:) | JS→Native | ○ | | dismissNavigationAlert(animated:) | JS→Native | ○ | | Navigation alert delegate events | Native→JS | ○ |

Search Template

| API | Kind | Status | | --- | --- | --- | | createSearchTemplate() | JS→Native | ● | | updateSearchResults(requestId, items) | JS→Native | ● | | addSearchTextListener(listener) | Native→JS | ● | | addSearchResultSelectedListener(listener) | Native→JS | ● | | addSearchButtonPressedListener(listener) | Native→JS | ● |

List Template

| API | Kind | Status | | --- | --- | --- | | CPListTemplate creation (title, sections) | JS→Native | ○ | | CPListSection (items, header) | Config | ○ | | CPListItem (text, detailText, image, handler) | Config | ○ | | CPListImageRowItem (text, images) | Config | ○ | | updateSections(_:) | JS→Native | ○ | | List item selection delegate | Native→JS | ○ | | Assistant cell configuration | Config | ○ |

Grid Template

| API | Kind | Status | | --- | --- | --- | | CPGridTemplate creation (title, gridButtons) | JS→Native | ○ | | CPGridButton (titleVariants, image, handler) | Config | ○ | | updateGridButtons(_:) | JS→Native | ○ |

Tab Bar Template

| API | Kind | Status | | --- | --- | --- | | CPTabBarTemplate creation (templates) | JS→Native | ○ | | updateTemplates(_:) | JS→Native | ○ | | Tab selection delegate | Native→JS | ○ |

Information Template

| API | Kind | Status | | --- | --- | --- | | CPInformationTemplate creation (title, layout, items, actions) | JS→Native | ○ | | CPInformationItem (title, detail) | Config | ○ | | CPTextButton (title, textStyle, handler) | Config | ○ |

Alert & Action Sheet Templates

| API | Kind | Status | | --- | --- | --- | | CPAlertTemplate (titleVariants, actions) | JS→Native | ○ | | CPActionSheetTemplate (title, message, actions) | JS→Native | ○ | | CPAlertAction (title, style, handler) | Config | ○ |

Voice Control Template

| API | Kind | Status | | --- | --- | --- | | CPVoiceControlTemplate (voiceControlStates) | JS→Native | ○ | | CPVoiceControlState (identifier, titleVariants, image) | Config | ○ | | activateVoiceControlState(withIdentifier:) | JS→Native | ○ |

Contact Template

| API | Kind | Status | | --- | --- | --- | | CPContactTemplate / CPContact | JS→Native | ○ | | Contact action buttons (call, directions, message) | Config | ○ |

Point of Interest Template

| API | Kind | Status | | --- | --- | --- | | CPPointOfInterestTemplate (title, pointsOfInterest) | JS→Native | ○ | | CPPointOfInterest (location, title, subtitle, pinImage) | Config | ○ | | Point of interest delegate events | Native→JS | ○ |

Now Playing Template

| API | Kind | Status | | --- | --- | --- | | CPNowPlayingTemplate.shared | Config | ○ | | Now playing buttons (rate, shuffle, repeat, etc.) | Config | ○ | | Now playing observer events | Native→JS | ○ | | Sports mode (iOS 18.4+) | Config | ○ |

Session Configuration

| API | Kind | Status | | --- | --- | --- | | CPSessionConfiguration (delegate) | Config | ○ | | Content style change events | Native→JS | ○ | | Limited user interface change events | Native→JS | ○ |

Dashboard & Instrument Cluster

| API | Kind | Status | | --- | --- | --- | | CPTemplateApplicationDashboardScene | Config | ○ | | CPDashboardController / CPDashboardButton | Config | ○ | | CPTemplateApplicationInstrumentClusterScene | Config | ○ | | CPInstrumentClusterController | Config | ○ |

Multitouch (iOS 26+)

| API | Kind | Status | | --- | --- | --- | | Zoom gestures (pinch, double-tap) | Native→JS | ○ | | Rotation gestures | Native→JS | ○ | | Pitch gestures | Native→JS | ○ |

How It Works

The module has three layers:

  1. TypeScript API (src/) — Typed functions and React hooks that consumers import from @bradford-tech/expo-carplay.
  2. Swift native module (ios/) — Bridges to Apple's CarPlay framework using Expo modules core. Handles CPTemplateApplicationSceneDelegate, CPMapTemplate, CPNavigationSession, MKMapView, and the event pipeline back to JS.
  3. Expo config plugin (plugin/) — Automatically configures Info.plist with the CarPlay scene manifest and adds the required entitlements at prebuild time.

The native layer manages the CarPlay map using MKMapView with native location tracking. Route polylines are rendered as colored MKPolyline overlays. Navigation sessions use CPTrip, CPManeuver, and CPTravelEstimates from the CarPlay framework.

Types

All types are exported from the package:

import type {
  BarButtonConfig,
  Coordinate,
  MapButtonConfig,
  MapTemplateConfig,
  ManeuverConfig,
  RouteSegment,
  SearchResultItem,
  TravelEstimates,
  TripConfig,
} from "@bradford-tech/expo-carplay";

Contributing

git clone https://github.com/bradford-tech/expo-carplay.git
cd expo-carplay
npm install

# Build the TypeScript library
npm run build

# Build the Expo config plugin
npm run build:plugin

# Format, lint, type-check
npm run fix

# Run the example app
npm run dev

# Open the iOS project in Xcode
npm run open:ios

License

MIT