Skip to main content

React SDK guide

The @foxglove/embed-react package provides the <FoxgloveViewer /> component, providing a React API for embedding Foxglove in your application.

Installation

Start by installing the @foxglove/embed-react package.

npm install --save @foxglove/embed-react

Basic setup

The @foxglove/embed-react package provides the <FoxgloveViewer /> component. This component is built on top of the @foxglove/embed package and provides the same capabilities, but with a more React-friendly API.

None of the props are required, but you should at least provide the orgSlug prop to ensure the user is signed into the correct organization. If you don't know your organization slug (or if you're using a fully self-hosted embedded viewer) you can omit this prop, allowing users to select the organization they want to sign into.

import { FoxgloveViewer } from "@foxglove/embed-react";

function BasicExample() {
return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer orgSlug="my-org-slug" />
</div>
);
}

Events

You can pass callbacks to the FoxgloveViewer component to be called when the viewer is ready or when an error occurs.

import { FoxgloveViewer } from "@foxglove/embed-react";
import { useState } from "react";

function EventsExample() {
const [isReady, setIsReady] = useState(false);
const [error, setError] = useState<string | undefined>(undefined);

return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer
orgSlug="my-org-slug"
onReady={() => {
setIsReady(true);
}}
onError={(err) => {
setError(err);
console.error("Error", err);
}}
/>
</div>
);
}

Changing the data source

The embedded viewer can be used to visualize data from a variety of sources (including files, live data sources, and recordings). The data source is set using the data prop.

The following example shows how to open a file picker and set the selected file as data source.

import { FoxgloveViewer } from "@foxglove/embed-react";
import { useState } from "react";

function EventsExample() {
const [dataSource, setDataSource] = useState<DataSource | undefined>(undefined);

return (
<div style={{ height: "100vh" }}>
<button
style={{ padding: 16 }}
onClick={async () => {
try {
const handles = await window.showOpenFilePicker({
multiple: false,
types: [
{
description: SUPPORTED_FILE_TYPES.join(", "),
accept: {
"application/octet-stream": SUPPORTED_FILE_TYPES,
},
},
],
});
const file = await handles[0].getFile();
setDataSource({
type: "file",
file,
});
} catch (err: unknown) {
throw new Error("Error opening file");
}
}}
>
Open file data source ...
</button>
<FoxgloveViewer orgSlug="my-org-slug" data={dataSource} />
</div>
);
}

You can also connect to a live data source, such as a Foxglove WebSocket, a ROS Bridge, or a file hosted on a remote server.

import { FoxgloveViewer } from "@foxglove/embed-react";

function LiveDataSourceExample() {
// Connect to a Foxglove WebSocket
return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer
orgSlug="my-org-slug"
data={{
type: "live",
// The protocol defined the type of live data source to connect to.
// Supported protocols are:
// - "foxglove-websocket" => Foxglove WebSocket
// - "rosbridge-websocket" => ROS Bridge
// - "remote-file" => File hosted on a remote server
protocol: "foxglove-websocket",
url: "ws://localhost:8765",
}}
/>
</div>
);
}

You can also visualize a recording stored in your Foxglove organization:

import { FoxgloveViewer } from "@foxglove/embed-react";

function RecordingDataSourceExample() {
// Connect to a recording
return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer
orgSlug="my-org-slug"
data={{
type: "recording",
recordingId: "rec_0dHVqSWhIQ8HUUJU",
projectId: "prj_0dX15xqpCP0yPYNq",
}}
/>
</div>
);
}

Another way to visualize a recording is by connecting to a device. You can provide the device ID or device name, along with the start and end times. Both start and end are required strings compatible with ISO8601/RFC3339.

Here are two examples of how to connect to a device:

import { FoxgloveViewer } from "@foxglove/embed-react";

function RecordingDataSourceExample() {
// Connect to a device by ID
return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer
orgSlug="my-org-slug"
data={{
type: "device",
deviceId: "dev_0dHVqSWhIQ8HUUJU",
start: "2025-01-01T00:00:00Z",
end: "2025-02-01T00:00:00Z",
}}
/>
</div>
);
}
import { FoxgloveViewer } from "@foxglove/embed-react";

function RecordingDataSourceExample() {
// Connect to a device by name
return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer
orgSlug="my-org-slug"
data={{
type: "device",
deviceName: "my-device",
start: "2025-01-01T00:00:00Z",
end: "2025-02-01T00:00:00Z",
}}
/>
</div>
);
}

Layout Management

Layouts are stored in the browser's local storage. The storageKey is used to identify the layout. Any changes made by the user will be restored when the layout is selected again. Setting the force parameter to true overrides the user's local changes.

Restore a layout with persisted changes

The following example shows how to restore a layout from local storage. If no layout with the same storage key is found, a default layout will be created and saved to the local storage under the provided storage key.

import { FoxgloveViewer } from "@foxglove/embed-react";

function RestoreDefaultLayoutExample() {
return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer
orgSlug="my-org-slug"
layout={{
storageKey: "layout-a",
}}
/>
</div>
);
}

You could also customize the fallback layout using the opaqueLayout parameter.

import { FoxgloveViewer } from "@foxglove/embed-react";

function RestoreCustomLayoutExample() {
return (
<div style={{ height: "100vh" }}>
<FoxgloveViewer
orgSlug="my-org-slug"
layout={{
storageKey: "layout-a",
opaqueLayout: {
/* Your layout data. This is an opaque JavaScript object, which should be parsed from a JSON layout file that was exported from the Foxglove app. */
},
}}
/>
</div>
);
}

Override local changes to a layout

The following example loads a layout from a JSON file and overrides the existing layout.

import { FoxgloveViewer, type SelectLayoutParams } from "@foxglove/embed-react";
import { useState } from "react";

function OverrideLayoutExample() {
const [layout, setLayout] = useState<SelectLayoutParams | undefined>(undefined);

return (
<div style={{ height: "100vh" }}>
<button
onClick={async () => {
const handles = await window.showOpenFilePicker({
multiple: false,
types: [
{
description: ".json",
accept: {
"application/json": [".json"],
},
},
],
});

const file = await handles[0].getFile();

setLayout({
storageKey: "layout-a",
opaqueLayout: JSON.parse(await file.text()),
force: true,
});
}}
>
Select layout
</button>
<FoxgloveViewer orgSlug="my-org-slug" layout={layout} />
</div>
);
}

Retrieve the current layout

Use getLayout to retrieve the currently configured layout, including any user modifications. The layout data can later be passed in to the opaqueLayout parameter.

import { FoxgloveViewer, type FoxgloveViewerInterface } from "@foxglove/embed-react";
import { useRef } from "react";

function GetLayoutExample() {
const viewerRef = useRef<FoxgloveViewerInterface>(null);
return (
<div style={{ height: "100vh" }}>
<button
onClick={() => {
const layoutData = await viewerRef.current?.getLayout();
// Save the layoutData
}}
>
Save layout
</button>
<FoxgloveViewer ref={viewerRef} ... />
</div>
);
}

API reference

Refer to the @foxglove/embed-react documentation for the full API reference.