Skip to content

React Template

The React template provides a frontend development environment using React, TypeScript, and Vite. It includes pre-configured platform integration utilities for data fetching, authentication handling, and asynchronous state management.

Project Structure

src/
├── main.tsx           # Application entry point
├── App.tsx            # Root component with routing
├── index.scss         # Global styles
├── lib/               # Platform utilities
│   ├── future/        # Future utilities for async state
│   ├── fetch/         # Data fetching with auth handling
│   └── ...            # Other platform utilities
└── components/        # Reusable UI components

Dependencies

The template uses the following key packages (defined in package.json):

  • React - UI library for building component-based interfaces
  • TypeScript - Type-safe JavaScript superset
  • Vite - Fast build tool and development server
  • React Router - Client-side routing
  • React Bootstrap - Pre-built UI components
  • Lucide React - Icon library
  • Sass - CSS preprocessor for styling

Setup Instructions

1. Install Dependencies

bash
npm install

2. Configure Environment Variables

Create a .env file in the project root with the required platform configuration. See Creating Your First Application for details on obtaining these values.

Development Workflow

Running the Development Server

bash
npm start

This starts the Vite development server on http://localhost:5173 (or next available port) with:

  • Hot Module Replacement (HMR) - Instant updates without full page reload
  • Fast refresh - Preserves component state during updates
  • API proxying - Automatically proxies backend and gateway API calls

Building for Production

bash
npm run build

This creates an optimized production bundle in the dist/ directory with:

  • Minified JavaScript and CSS
  • Tree-shaking to remove unused code
  • Source maps for debugging
  • Optimized asset loading

Preview Production Build

bash
npm run preview

Serves the production build locally for testing before deployment.

Vite Configuration

The template uses Vite for fast development and optimized builds. Key configuration features in vite.config.ts:

API Proxying

Vite automatically proxies API requests during development:

TypeScript
ts
proxy: {
  "/reload": backend,
  "^/api/(?!gw/)": {
    target: backend,
    headers: {
      Authorization: `Bearer ${process.env.APP_BACKEND_REQUEST_TOKEN}`,
    },
  },
  "/api/gw/": {
    target: process.env.APP_PLATFORM_URL,
    changeOrigin: true,
    headers: {
      Authorization: `Bearer ${process.env.APP_FRONTEND_TOKEN}`,
    },
  },
},

Benefits:

  • No CORS issues during development
  • Automatic authentication header injection
  • Seamless backend integration

Plugins

  • @vitejs/plugin-react - Enables React Fast Refresh
  • vite-tsconfig-paths - Resolves TypeScript path aliases (e.g., @/lib)

SCSS Support

The template includes Sass preprocessing for advanced styling capabilities.


Platform Integration

The template includes utilities for interacting with backend APIs and the platform gateway. These utilities handle authentication, loading states, and error handling automatically.

Data Fetching

This section describes the available options for fetching data in the React frontend template. The platform provides two main approaches: the useFetch hook for manual control and the WithFetch component for automatic state management.

useFetch Hook

The useFetch hook is a React hook that fetches data from a URL and automatically handles authentication redirects. It returns a Future object that represents the current state of the fetch operation. You can find more information about futures further down on this page.

Usage

React
tsx
const future = useFetch<WeatherData>(`/api/weather/${location}`);

return (
  <WithFuture future={future}>
    {(weather) => <WeatherCard data={weather} />}
  </WithFuture>
);

Parameters

  • url (string): The URL to fetch data from
  • options (RequestInit, optional): Optional fetch configuration options
  • deps (DependencyList, optional): Additional dependencies that trigger re-fetching

Return Value

The hook returns a Future<T> object with the following possible states:

  • loading: Data is currently being fetched
  • error: An error occurred
  • success: Data was successfully fetched (available via future.data)

How It Works

  1. Executes an HTTP request to the specified URL
  2. On a 403 status with Location header, automatically redirects to the location
  3. After successful authentication, the user is returned to the application
  4. Automatically parses the response as JSON or text based on the Content-Type header

WithFetch Component

The WithFetch component simplifies data fetching by automatically handling loading and error states by replacing child components with a spinner until the data is loaded.

Usage

React
tsx
return (
  <WithFetch<WeatherData> url={`/api/weather/${location}`}>
    {(data) => <WeatherCard data={data} />}
  </WithFetch>
);

Props

  • url (string): The URL to fetch data from
  • options (RequestInit, optional): Optional fetch configuration options
  • children (function): Render function that receives the fetched data

Behavior

  • Automatically displays a spinner while loading
  • Automatically displays an error message on failure
  • Calls the children function with the data once available

Authentication

WithFetch aswell as useFetch automatically handle authentication redirects:

  1. On a 403 response with Location header, the user is redirected to the provided location
  2. The user is redirected for authentication
  3. After successful authentication, the original request is automatically retried

This works for both backend APIs (/api/...) and Platform Gateway calls (/api/gw/...).

Reloading/Refreshing data

By default, data is fetched when the page is loaded and whenever the URL changes. If data needs to be reloaded, for example after an API call to modify some data, the easiest way is to use the global reload:

React
tsx
return (
  <WithFetch<string> url={`/api/current-time`}>
    {(data) => (
      <>
        Now: {data}
        <Button onClick={() => globalReload()}>Reload</Button>
      </>
    )}
  </WithFetch>
);

Calling the globalReload() function will cause all data loaded unsing useFetch() and <WithFetch/> to be reloaded. If more fine grained reloading is required, useReload() can be used together with the deps option:

React
tsx
const reload = useReload();
return (
  <WithFetch<string> url={`/api/current-time`} options={{ deps: [reload] }}>
    {(data) => (
      <>
        Now: {data}
        <Button onClick={() => reload.trigger()}>Reload</Button>
      </>
    )}
  </WithFetch>
);

Of course, the deps option can be used with any other object. It is internally passed to the deps array of a useEffect.

The last option for data refreshing is polling:

React
tsx
return (
  <WithFetch<string> url={`/api/current-time`} options={{ poll: 1000 }}>
    {(data) => <>Now: {data}</>}
  </WithFetch>
);

This polls the data every x milliseconds.

Future Utilities

The Future Utilities provides a comprehensive set of utilities for handling asynchronous operations in React applications. It's built around the Future type, which represents an async operation that can be in one of three states: loading, error, or complete.

Concept

The Future pattern is a type-safe way to handle async operations. Instead of managing loading states, error states, and data separately, a Future encapsulates all three states in a discriminated union:

ts
export type Future<T> =
  | { state: "loading" }
  | { state: "complete"; data: T }
  | { state: "error"; error: any };

Utilities

mapFuture

Transforms the data of a Future using a synchronous function.

tsx
const simplifiedWeather = mapFuture(weatherFuture, (data) => ({
  location: data.location.name,
  temperature: data.current.temp_c,
  condition: data.current.condition.text,
}));

Parameters:

  • future: The Future to transform
  • fn: Function to transform the data if the Future is complete

Returns: A new Future with the transformed data

Use Case: Transform data if the future is in its completed state without triggering re-renders or side effects.

Hooks

usePromise

Wraps a promise factory and returns a Future with loading, error, and success states.

tsx
const promiseFuture = usePromise(async () => {
  const response = await fetch(`/api/weather/${location}`);
  return response.json() as Promise<WeatherData>;
}, [location]);

Parameters:

  • factory: Function that returns a Promise to execute
  • deps: Dependency array that triggers re-execution when changed

Returns: A Future<T> representing the promise state

Use Case: Convert any promise-based async operation into a Future.

useFutureEffect

Runs an effect when a Future completes successfully.

tsx
const weatherFuture = useFetch<WeatherData>(`/api/weather/${location}`);

useFutureEffect(
  weatherFuture,
  (data) => {
    setEffectLog((prev) => [
      ...prev,
      `Weather loaded for ${data.location.name}`,
    ]);
  },
  [],
);

Parameters:

  • input: The Future to watch
  • effect: Effect function to run when the Future completes successfully
  • deps: Additional dependencies for the effect

Use Case: Trigger side effects (logging, analytics, notifications) when async operations complete.

useFutureEffects

Runs an effect when all Futures complete successfully.

tsx
const londonWeather = useFetch<WeatherData>("/api/weather/London");
const parisWeather = useFetch<WeatherData>("/api/weather/Paris");

useFutureEffects(
  { london: londonWeather, paris: parisWeather },
  ({ london, paris }) => {
    setEffectLog((prev) => [
      ...prev,
      `Both cities loaded: ${london.location.name} & ${paris.location.name}`,
    ]);
  },
  [],
);

Parameters:

  • inputs: Object containing multiple Futures to watch
  • effect: Effect function to run when all Futures complete successfully
  • deps: Additional dependencies for the effect

Use Case: Coordinate side effects that depend on multiple async operations completing.

useFutureMemo

Memoizes a synchronous transformation of a Future.

tsx
const temperatureFuture = useFutureMemo(
  weatherFuture,
  (data) => ({
    celsius: data.current.temp_c,
    fahrenheit: data.current.temp_f,
    location: data.location.name,
  }),
  [],
);

Parameters:

  • input: The Future to transform
  • fn: Function to transform the data if the Future is successful
  • deps: Additional dependencies that trigger re-computation

Returns: A memoized Future with the transformed data

Use Case: Derive computed values from futures without unnecessary re-computations.

useFutureMemos

Memoizes a transformation of multiple Futures.

tsx
const combinedWeather = useFutureMemos(
  { london: londonWeather, paris: parisWeather },
  ({ london, paris }) => ({
    avgTemp: (london.current.temp_c + paris.current.temp_c) / 2,
    cities: [london.location.name, paris.location.name],
  }),
  [],
);

Parameters:

  • inputs: Object containing multiple Futures to combine
  • fn: Function to transform the combined data when all Futures are successful
  • deps: Additional dependencies that trigger re-computation

Returns: A memoized Future with the transformed combined data

Use Case: Combine and transform data from multiple async sources efficiently.

useFutureMap

Maps a Future through an asynchronous transformation function.

tsx
const enrichedWeather = useFutureMap(
  weatherFuture,
  async (data) => {
    // Simulate an async transformation
    await new Promise((resolve) => setTimeout(resolve, 100));
    return {
      ...data,
      enriched: true,
      timestamp: new Date().toISOString(),
    };
  },
  [],
);

Parameters:

  • input: The Future to transform
  • fn: Async function to transform the data if the Future is successful
  • deps: Additional dependencies that trigger re-execution

Returns: A Future representing the async transformation

Use Case: Chain async operations, such as fetching data and then processing it with another async call.

Components

WithFutures

Component that waits for multiple Futures to complete and renders children with the combined data.

React
tsx
const weather1 = useFetch<WeatherData>("/api/weather/London");
const weather2 = useFetch<WeatherData>("/api/weather/Paris");
React
tsx
}
<WithFutures futures={{ weather1, weather2 }}>
  {({ weather1, weather2 }) => (
    <Stack gap={3}>
      <WeatherCard data={weather1} />
      <WeatherCard data={weather2} />
    </Stack>
  )}
</WithFutures>
{

Props:

  • futures: Object containing multiple Futures to wait for
  • children: Render function that receives the combined data from all Futures

Behavior:

  • Displays a spinner while any Future is loading
  • Displays an error alert if any Future fails
  • Calls the children function with combined data once all Futures are complete

Use Case: Declaratively render UI that depends on multiple async operations.

When to Use What?

Use usePromise when:

  • You need to convert existing promise code to Futures
  • You want full control over the promise execution

Use useFutureEffect when:

  • You need to trigger side effects on completion
  • You need to coordinate with external systems

Use useFutureMemo when:

  • You need to derive computed values from async data
  • You want to avoid unnecessary re-computations
  • The transformation is synchronous

Use useFutureMap when:

  • You need to chain async operations
  • You want to process data asynchronously

Use WithFutures when:

  • You need to wait for multiple async operations
  • You want declarative rendering based on async state

Integration with Data Fetching

The Future Utilities integrate seamlessly with the data fetching utilities:

React
tsx
const integrationWeatherFuture = useFetch<WeatherData>("/api/weather/London");

const integrationTempFuture = useFutureMemo(
  integrationWeatherFuture,
  (data) => data.current.temp_c,
  [],
);

Next Steps