OTP-UI React Component Library

June 8, 2026 Β· View on GitHub

Join the chat at https://gitter.im/opentripplanner/otp-react-redux Build process badge

Description

React component library, which can be used to build trip planner webapps.

See:

Getting Started

 git checkout https://github.com/opentripplanner/otp-ui.git
 pnpm install
 pnpm dev

Development

You can chat with the main OTP-RR developers in our Gitter chat. Support is not guaranteed, but we may be able to answer questions and assist people wishing to make contributions.

Some packages in otp-ui depend on sibling packages (e.g., @opentripplanner/core-utils is used by many of its siblings). Internal dependencies are handled with the workspace:* version, which is a notation provided by pnpm. This allows us to always reference the current internal dependency version. Therefore, before Storybook can be run or packages published, it's necessary to run pnpm build-all so that all internal packages are built first.

If the Storybook addon bar (a bar of controls at the bottom of the story) does not appear, you may need to clear localStorage by opening the browser console and typing localStorage.clear().

Storyshot testing

This repo utilizes the Storyshot Storybook addon to perform snapshot tests of every story in this monorepo. Whenever the script pnpm unit is run, the Storyshot addon will be included along with all the other tests. It will compare the initial output of every story to the saved snapshot of that story. This provides a quick way to make sure nothing drastic has changed and that every single story is able to initially render without an error. Storyshot doesn't snapshot all possible changes that can be done while interacting with story components. Often times these snapshots will need to be updated and that can be accomplished by running pnpm update-snapshots.

Stack

A Monorepo with multiple packages and a shared build, test, and release process.

  • πŸ‰ Lerna β€Š- The Monorepo manager
  • πŸ“¦ PNPM Workspacesβ€Š - β€ŠSane multi-package management
  • πŸš€ Reactβ€Š - β€ŠJavaScript library for user interfaces
  • πŸ’… styled-componentsβ€Š -β€Š CSS in JS elegance
  • πŸ›  Babelβ€Š - β€ŠCompiles next-gen JavaScript
  • πŸ“– Storybook - UI Component Environment
  • πŸƒ Jestβ€Š -β€Š Unit/Snapshot Testing

Usage

  • pnpm dev - Starts Storybook for viewing all the components locally.
  • pnpm install - Installs all of the packages and links dependent packages together.
  • pnpm build-all - Compiles TypeScript, then builds CJS (/lib) and ESM (/esm) outputs for all packages.
  • pnpm build:cjs - Builds CommonJS (/lib) output for all packages.
  • pnpm build:esm - Builds ES Module (/esm) output for all packages.
  • pnpm typescript - Runs TypeScript type checking across all packages.
  • pnpm test - Runs the full test suite: linting, i18n checks, TypeScript, unit tests, Storybook tests, and accessibility tests.
  • pnpm unit - Runs Jest unit tests only.
  • pnpm coverage - Shows Jest unit test coverage.
  • pnpm lint - Runs all linters (JS/TS, styles, and GraphQL).
  • pnpm lint:js - Lints JS/TS files with ESLint.
  • pnpm lint:styles - Lints styled-components with Stylelint.
  • pnpm lint:graphql - Lints GraphQL files.
  • pnpm build-storybook - Builds the Storybook static site.
  • pnpm test-storybook - Runs Storybook interaction tests (requires Storybook running on port 5555).
  • pnpm update-snapshots - Builds Storybook and updates all snapshot tests.
  • pnpm check:i18n-all - Checks i18n integrity for all languages.
  • pnpm check:i18n-en-fr - Checks i18n integrity for English (US) and French.
  • pnpm clean - Deletes all files in the gitignore (note: this can delete local editor settings).
  • pnpm pack-all - Builds all packages and creates tarball packages.
  • npx lerna changed - Show which packages have changed.
  • npx lerna diff - Show specifically what files have cause the packages to change.
  • npx lerna create <packageName> - Creates new package and walks through setting up package.json

Releasing

This project uses semantic-release to create releases to NPM. It is expect that contributors create Conventional Commit messages. These are then parsed by semantic-release which will automatically create an appropriate release for each package whenever a branch is merged to master.

Local Testing

Internal package dependencies are referenced using the workspace protocol provided by pnpm. This allows us to depend on our internal packages without keeping versions up to date, but these versions must be replaced with the actual version numbers prior to release. pnpm handles this when publishing automatically. However, if you wish to rely on an otp-ui package in a local filesystem project using the file protocol, you need to use pnpm pack to create a tarball of the package, then reference that tarball in the other project's package.json.

For example, to depend on core-utils locally, you can run pnpm pack from within the packages/core-utils. Next, in the other project, use a line like this to reference the resulting tarball. "@opentripplanner/core-utils": "file:../otp-ui/packages/core-utils/opentripplanner-core-utils-12.0.2.tgz",

Package Tarball Creation

The pnpm pack-all command creates tarball packages for all non-private packages in the monorepo and displays their locations. This is useful for testing packages locally in another project.

When executed, this command runs pnpm pack for each non-private package and outputs the path to the generated tarball file. You can then reference these tarballs directly in other projects using the file protocol in your other project's package.json.

Raster Tile Versions

As of Fall 2022, the otp-ui map layers have migrated from Leaflet to MapLibreGL. This migration was a breaking change, so existing uses of otp-ui should be unaffected. If you wish to migrate to the latest version, please see the Migration Guide.

We understand not all will want to upgrade to vector tiles right away, and so will be maintaining the raster tile versions of all relevant packages for the foreseeable future.

The following table lists the last major version of each package which uses raster tiles. These major versions will receive fresh minor versions as updates are needed.

PackageLatest Major Version with Raster Tiles
base-map2
core-utils7
endpoints-overlay1
itinerary-body4
park-and-ride-overlay1
route-viewer-overlay1
stop-viewer-overlay1
stops-overlay4
transit-vehicle-overlay2
transitive-overlay2
trip-viewer-overlay1
types3
vehicle-rental-overlay1
zoom-based-markers1

Internationalization

OTP-UI uses react-intl from the formatjs library for internationalization. Both react-intl and formatjs take advantage of native internationalization features provided by web browsers.

Language-specific content is located in YML files under the i18n folder of packages that have internationalizable content (e.g. en-US.yml for American English, fr.yml for generic French, etc.).

Note: Do not add comments to these YML files! Comments are removed by yaml-sort during pre-commit. Instead, comments for other developers should be placed in the corresponding js/jsx/ts/tsx file. Comments for translators should be entered into Weblate (see Contributing Translations)

To use the YML files in your react-intl application:

  • Merge the content of this file into the messages object that has your other localized strings,
  • Flatten the ids, i.e. convert a structure such as
      otpUi > ItineraryBody > travelByMode > bike
    
    into
      otpUi.ItineraryBody.travelByMode.bike
    
  • Pass the resulting object to the messages prop of IntlProvider. See packages/from-to-location-picker/src/index.story.tsx for an example of how to initialize localized messages with IntlProvider.

Using internationalized content in the code

Access the internationalized content in code, typically using either

import { FormattedMessage } from "react-intl";
...
<FormattedMessage id="..." />

or, if you need a string,

// Obtain `intl` using `injectIntl` or `useIntl`.
intl.formatMessage({ id: ... })

where the id passed to FormattedMessage and intl.formatMessage can be literal or a computed value. See Internationalization checks and reporting for caveats.

Internationalization checks and reporting

Code and translation integrity is checked by scripts that you can run locally. check:i18n-all checks for all languages. check:i18n-en-fr checks for English (US) and French and is run on GitHub after each push.

These scripts check the following:

  • All entries in the applicable translation files are used in the code.
  • All message ids used in the code have translations.

For the scripts to work best, you should use literal ids as much as possible with <FormattedMessage> or intl.formatMessage. This is because the scripts use the formatJS CLI, and the formatJS CLI simply ignores message ids that are not literals.

Exceptions to checks

Exceptions to the checks above can be defined when:

  • Reusing a message defined in another package,
  • A message id needs to be computed, with some portion of it coming from a parameter, and implementing a switch case does not provide substantial benefits.

Exceptions are defined in optional files named i18n-exceptions.json. See the scripts package README for setting these files up.

Contributing translations

OTP-UI now uses Hosted Weblate to manage translations!

Translation status
Translation status for OTP-react-redux and OTP-UI on Hosted Weblate

Translations from the community are welcome and very much appreciated, please see instructions at https://hosted.weblate.org/projects/otp-react-redux/. Community input from Weblate will appear as pull requests with changes to files in the applicable i18n folders for our review. (Contributions may be edited or rejected to remain in line with long-term project goals.)

If changes to a specific language file is needed but not enabled in Weblate, please open an issue or a pull request with the changes needed.