Web Application
Web Bluetooth

Web Bluetooth


In this section, we will guide you through the creation of an application. This application will connect to your Ledger Nano X to display the address of your account (eg. bitcoin account, ethereum account).


Before starting, make sure you have gone through the prerequisites.

Web App Bluetooth (only Nano X)

The Ledger Nano S and S Plus do not have the Bluetooth functionality. This tutorial will only work with a Ledger Nano X.

Please be aware that the Bluetooth implementation is only supported by a few browsers. You can check the browser support (opens in a new tab) for the Web Bluetooth.

Project Initialization

This web application is build with React, which is a frontend Javascript framework.

First, open a terminal and create a new project. For this tutorial the project will be named “examples-web-bluetooth”.


npx create-react-app examples-web-bluetooth
cd examples-web-bluetooth

Open the folder in an editor. The React app initialization creates a "src" folder where you will find all the code.


touch ./src/QRCode.js
touch ./src/polyfill.js

Your folder must look like this.

Folder of the Application Fig. 1: Folder of the Application

For this implemetation, you will only modify "App.js", "App.css”, "index.js", "polyfill.js" and “QRCode.js”.

Code Implementation


In "polyfill.js" copy-paste the following code:

global.Buffer = require("buffer").Buffer;


In "index.js" add the following code:

import './polyfill'


In App.js copy-paste the following code:

import React, { Component } from "react";
import TransportWebBLE from "@ledgerhq/hw-transport-web-ble";
import AppEth from "@ledgerhq/hw-app-eth";
import QRCode from "./QRCode";
import "./App.css";
const delay = ms => new Promise(success => setTimeout(success, ms));
class DeviceSelectionScreen extends Component {
  //Component that will display all the Ledger Nano X that is recognized by bluetooth
  state = {
    devices: []
  createBLE = async () => {
    const transport = await TransportWebBLE.create();
  render() {
    return (
      <div className="DeviceSelectionScreen">
          Power up your Ledger Nano X and enter your pin before continuing...
        <button onClick={this.createBLE}>Connect with Bluetooth</button>
class ShowAddressScreen extends Component {
  //Component that is responsible to display your ethereum address
  state = {
    error: null,
    address: null
  async componentDidMount() {
    while (!this.state.address) {
      if (this.unmounted) return;
      await this.fetchAddress(false);
      await delay(500);
  async componentWillUnmount() {
    this.unmounted = true;
  fetchAddress = async verify => {
    const { transport } = this.props;
    try {
      const eth = new AppEth(transport);
      const path = "44'/60'/0'/0/0"; // HD derivation path
      const { address } = await eth.getAddress(path, verify);
      if (this.unmounted) return;
      this.setState({ address });
    } catch (error) {
      // in this case, user is likely not on Ethereum app
      console.warn("Failed: " + error.message);
      if (this.unmounted) return;
      this.setState({ error });
      return null;
  render() {
    const { address, error } = this.state;
    return (
      <div className="ShowAddressScreen">
        {!address ? (
            <p className="loading">Loading your Ethereum address...</p>
            {error ? (
              <p className="error">
                A problem occurred, make sure to open the Ethereum application
                on your Ledger Nano X. (
                {String((error && error.message) || error)})
            ) : null}
        ) : (
            <strong>Ledger Live Ethereum Account 1</strong>
            <QRCode data={address} size={300} />
class App extends Component {
  state = {
    transport: null
  onSelectDevice = transport => {
    window.ledgerTransport = transport;
    transport.on("disconnect", () => {
      this.setState({ transport: null });
    this.setState({ transport });
  render() {
    const { transport } = this.state;
    return (
      <div className="App">
        {!transport ? (
          <DeviceSelectionScreen onSelectDevice={this.onSelectDevice} />
        ) : (
          <ShowAddressScreen transport={transport} />
export default App;


In App.css copy-paste the following code:

.App {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
.App .DeviceSelectionScreen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
.App .ShowAddressScreen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
.App .error {
  color: #a00;
.App .ShowAddressScreen strong {
  margin: 1em;


In "QRCode.js" copy-paste the following code:

import React, { PureComponent } from "react";
import qrcode from "qrcode";
type Props = {
  data: string,
  errorCorrectionLevel: string,
  size: number,
  style?: *
export default class QRCode extends PureComponent<Props> {
  static defaultProps = {
    size: 200,
    errorCorrectionLevel: "H"
  componentDidMount() {
  componentDidUpdate() {
  _canvas = null;
  drawQRCode() {
    const { data, size, errorCorrectionLevel } = this.props;
    qrcode.toCanvas(this._canvas, data, {
      width: size,
      margin: 0,
      color: {
        light: "#ffffff"
  render() {
    return <canvas style={} ref={n => (this._canvas = n)} />;

Some error will be emphasized by your editor because of the 'QRCode.js' use some features from typescript. But you can skip those errors as they will not affect the application.

Dependencies Installation


npm install --save qrcode
npm install --save @ledgerhq/hw-app-eth
npm install --save @ledgerhq/hw-transport-web-ble
npm install --save buffer
npm install --save node-polyfill-webpack-plugin
npm install --save stream
PackageWhat does it do?
qrcode (opens in a new tab)It allows you to create a QR code.
@ledgerhq/hw-app-eth (opens in a new tab)It will help you ask your Nano to access the ethereum address.
@ledgerhq/hw-transport-web-ble (opens in a new tab)It provides you with all the methods to interact with your Ledger Nano X with a Bluetooth connexion.
buffer (opens in a new tab)The goal is to provide an API that is 100% identical to node's Buffer API.
node-polyfill-webpack-plugin (opens in a new tab)Polyfill Node.js core modules in Webpack. This module is only needed for webpack 5+.
stream (opens in a new tab)Node.js streams in the browser. Ported straight from the Node.js core and adapted to component/emitter's api.

Package.json Dependencies

Now that the dependencies are installed you can find them in the “package.js”. This is how your “package.json” has to look like:

  "name": "examples-web-bluetooth",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@ledgerhq/hw-app-eth": "^6.26.0",
    "@ledgerhq/hw-transport-web-ble": "^6.24.1",
    "@testing-library/jest-dom": "^5.16.1",
    "@testing-library/react": "^12.1.2",
    "@testing-library/user-event": "^13.5.0",
    "buffer": "^6.0.3",
    "eip55": "^2.1.0",
    "node-polyfill-webpack-plugin": "^1.1.4",
    "qrcode": "^1.5.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "5.0.0",
    "stream": "^0.0.2",
    "web-vitals": "^2.1.3"
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  "eslintConfig": {
    "extends": [
  "browserslist": {
    "production": [
      "not dead",
      "not op_mini all"
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"

Web App Test

Start the Development Server

Make sure the Bluetooth feature of your Nano is enabled.


If it was not enabled, you need to enable it and then power off the power on your Nano.

Now that the Setup is finished, the app has to be available to be displayed. Therefore start the development server:

npm run start

All the browser do not support the Bluetooth please look at the browser support .

Now the application is up and running. Open the browser and go to localhost:3000, it will display :

Application Running on Browser Fig. 2: Application Running on Browser

Launch Ethereum App

Before clicking on the button, unlock it and run the Ethereum application. The steps are described below.

Nano Enter Code Pin
Fig. 4: Nano Enter Code Pin

Embedded Application
Fig. 5: Embedded Application

Embedded App is Running
Fig. 6: The App is running

Connect Your Nano to the Application

Now you can click on the button and a popup will be prompt. Choose your Ledger Nano X and click connexion

Connect the Nano with Bluetooth Fig. 7: Connect the Nano with Bluetooth

Then another popup will be prompt to ask you to confirm the pairing

Request of Pairing Fig. 8: Request of Pairing

Finally, if all goes well the address must be displayed with the QR code

Address Account Displayed Fig. 9: Address Account Displayed

Verify the Address on Nano

For security reasons, the address will also be displayed on your Ledger Nano X to verify and confirm the address.

Nano Verify Screen
Fig. 10: Nano Verify Screen

Nano Verify Address Screen
Fig. 11: Nano Verify Address Screen

Embedded Approve Screen
Fig. 12: Embedded Approve Screen

Congratulations you have successfully built your first application connected with Ledger Nano X!

Copyright © Ledger SAS. All rights reserved. Ledger, Ledger Nano S, Ledger Vault, Bolos are registered trademarks of Ledger SAS