React (JavaScript library)

Build TelemetryOS applications with React using the SDK's React-specific hooks and components

React Development

React is an open-source JavaScript library for building user interfaces, developed and maintained by Meta. React uses a component-based architecture and a virtual DOM to efficiently render and update UI elements, making it ideal for building interactive, data-driven applications. The TelemetryOS SDK provides first-class React support through custom hooks and components.

Build TelemetryOS applications with React using the SDK's React-specific hooks and components. The SDK provides optimized React integrations for store state management and Settings UI components that match the Studio design system.

Prerequisites

React Imports

The SDK provides two import paths:

// Core SDK (works with any framework)
import { configure, store, proxy, media } from '@telemetryos/sdk'

// React-specific hooks and components
import { useStoreState, createUseStoreState, SettingsContainer } from '@telemetryos/sdk/react'

Project Structure

project-root/
├── telemetry.config.json       # Platform configuration
├── package.json
├── tsconfig.json
├── vite.config.ts
├── index.html
└── src/
    ├── main.tsx                # Entry point (configure SDK here)
    ├── App.tsx                 # Routing logic
    ├── views/
    │   ├── Settings.tsx        # /settings mount point
    │   └── Render.tsx          # /render mount point
    ├── components/             # Reusable components
    ├── hooks/
    │   └── store.ts            # Store state hooks (createUseStoreState)
    ├── types/                  # TypeScript interfaces
    └── utils/                  # Helper functions

Complete File Implementations

src/main.tsx (Entry Point)

import { configure } from '@telemetryos/sdk';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';

// Configure SDK ONCE before React renders
// Name must match telemetry.config.json
configure('app-name');

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

src/App.tsx (Routing)

import Settings from './views/Settings';
import Render from './views/Render';

export default function App() {
  const path = window.location.pathname;

  if (path === '/settings') return <Settings />;
  if (path === '/render') return <Render />;

  return <div>Invalid mount point: {path}</div>;
}

src/hooks/store.ts (Store Hooks)

import { createUseStoreState } from '@telemetryos/sdk/react'

// Create typed hooks for each store key
export const useTeamStoreState = createUseStoreState<string>('team', '')
export const useLeagueStoreState = createUseStoreState<string>('league', 'nfl')

src/views/Settings.tsx (Complete Reference)

import { store } from '@telemetryos/sdk'
import {
  SettingsContainer,
  SettingsField,
  SettingsLabel,
  SettingsInputFrame,
  SettingsSelectFrame,
} from '@telemetryos/sdk/react'
import { useTeamStoreState, useLeagueStoreState } from '../hooks/store'

export default function Settings() {
  const [isLoadingTeam, team, setTeam] = useTeamStoreState(store().instance)
  const [isLoadingLeague, league, setLeague] = useLeagueStoreState(store().instance)

  return (
    <SettingsContainer>
      <SettingsField>
        <SettingsLabel>Team Name</SettingsLabel>
        <SettingsInputFrame>
          <input
            type="text"
            placeholder="Enter team name..."
            disabled={isLoadingTeam}
            value={team}
            onChange={(e) => setTeam(e.target.value)}
          />
        </SettingsInputFrame>
      </SettingsField>

      <SettingsField>
        <SettingsLabel>League</SettingsLabel>
        <SettingsSelectFrame>
          <select
            disabled={isLoadingLeague}
            value={league}
            onChange={(e) => setLeague(e.target.value)}
          >
            <option value="nfl">NFL</option>
            <option value="nba">NBA</option>
            <option value="mlb">MLB</option>
            <option value="nhl">NHL</option>
          </select>
        </SettingsSelectFrame>
      </SettingsField>
    </SettingsContainer>
  )
}

src/views/Render.tsx (Complete Reference)

import { useEffect, useState } from 'react'
import { proxy } from '@telemetryos/sdk'
import { store } from '@telemetryos/sdk'
import { useTeamStoreState, useLeagueStoreState } from '../hooks/store'

interface GameScore {
  homeTeam: string
  awayTeam: string
  homeScore: number
  awayScore: number
  status: string
}

export default function Render() {
  // Use same hooks as Settings - automatically syncs when Settings changes
  const [isLoadingTeam, team] = useTeamStoreState(store().instance)
  const [isLoadingLeague, league] = useLeagueStoreState(store().instance)

  const [score, setScore] = useState<GameScore | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  // Fetch scores when config changes
  useEffect(() => {
    if (!team) return

    const fetchScore = async () => {
      setLoading(true)
      setError(null)

      try {
        // Platform handles caching automatically - no manual cache needed
        const response = await proxy().fetch(
          `https://api.sportsdata.io/v3/${league}/scores/json/GamesByTeam/${team}`
        )

        if (!response.ok) throw new Error(`API error: ${response.status}`)

        const data = await response.json()
        if (data.length > 0) {
          const game = data[0]
          setScore({
            homeTeam: game.HomeTeam,
            awayTeam: game.AwayTeam,
            homeScore: game.HomeScore,
            awayScore: game.AwayScore,
            status: game.Status,
          })
        }
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error')
      } finally {
        setLoading(false)
      }
    }

    fetchScore()
  }, [team, league])

  // Loading state
  if (isLoadingTeam || isLoadingLeague) return <div>Loading config...</div>
  if (!team) return <div>Configure team in Settings</div>
  if (loading && !score) return <div>Loading scores...</div>
  if (error && !score) return <div>Error: {error}</div>

  return (
    <div>
      <h1>{team} - {league.toUpperCase()}</h1>
      {score && (
        <div>
          <div>{score.awayTeam} @ {score.homeTeam}</div>
          <div>{score.awayScore} - {score.homeScore}</div>
          <div>{score.status}</div>
        </div>
      )}
    </div>
  )
}

Quick Links