> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/BintzGavin/helios/llms.txt
> Use this file to discover all available pages before exploring further.

# React integration

> Use Helios with React for declarative video animations

Helios provides first-class support for React through hooks and components that integrate seamlessly with React's declarative programming model.

## Quick start

<Steps>
  <Step title="Install dependencies">
    ```bash theme={null}
    npm install @helios-project/core react react-dom
    ```
  </Step>

  <Step title="Create the useVideoFrame hook">
    ```tsx hooks/useVideoFrame.ts theme={null}
    import { useState, useEffect } from 'react';
    import { Helios } from '@helios-project/core';

    export function useVideoFrame(helios: Helios) {
        const [frame, setFrame] = useState(helios.getState().currentFrame);

        useEffect(() => {
            // Update local state when helios state changes
            const update = (state: { currentFrame: number }) => setFrame(state.currentFrame);

            // Subscribe returns an unsubscribe function
            return helios.subscribe(update);
        }, [helios]);

        return frame;
    }
    ```
  </Step>

  <Step title="Create your first animation">
    ```tsx App.tsx theme={null}
    import { Helios } from '@helios-project/core';
    import { useVideoFrame } from './hooks/useVideoFrame';

    const duration = 5;
    const fps = 30;
    const helios = new Helios({ duration, fps });

    // Bind to document timeline so it can be driven by the player/renderer
    helios.bindToDocumentTimeline();

    // Expose to window for debugging/player control
    if (typeof window !== 'undefined') {
        (window as any).helios = helios;
    }

    export default function App() {
        const frame = useVideoFrame(helios);

        // Calculate animation values
        const progress = frame / (duration * fps);
        const opacity = Math.min(1, frame / 30);
        const scale = 1 + Math.sin(progress * Math.PI * 4) * 0.2;
        const rotation = progress * 360;

        return (
            <div style={{
                display: 'flex',
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center',
                height: '100%',
                color: 'white',
                fontFamily: 'sans-serif'
            }}>
                <div style={{
                    width: 200,
                    height: 200,
                    backgroundColor: '#61dafb',
                    borderRadius: 20,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    opacity: opacity,
                    transform: `scale(${scale}) rotate(${rotation}deg)`,
                    boxShadow: '0 0 20px rgba(97, 218, 251, 0.5)'
                }}>
                    <span style={{
                        fontSize: '2rem',
                        fontWeight: 'bold',
                        color: '#282c34',
                        transform: `rotate(-${rotation}deg)`
                    }}>
                        React
                    </span>
                </div>

                <div style={{ marginTop: 40, textAlign: 'center' }}>
                    <p>Frame: {frame.toFixed(2)}</p>
                    <p>Progress: {(progress * 100).toFixed(1)}%</p>
                    <p>FPS: {fps}</p>
                </div>
            </div>
        );
    }
    ```
  </Step>
</Steps>

## Animation approaches

<Tabs>
  <Tab title="DOM animations">
    Animate React components using inline styles and the `useVideoFrame` hook:

    ```tsx theme={null}
    import { useVideoFrame } from './hooks/useVideoFrame';

    export default function DOMAnimation() {
        const frame = useVideoFrame(helios);
        const progress = frame / (duration * fps);

        return (
            <div style={{
                opacity: Math.min(1, frame / 30),
                transform: `translateX(${progress * 500}px) rotate(${frame * 2}deg)`
            }}>
                Animated content
            </div>
        );
    }
    ```
  </Tab>

  <Tab title="Canvas animations">
    Use refs and `useEffect` to draw frame-by-frame animations:

    ```tsx theme={null}
    import { useRef, useEffect } from 'react';
    import { useVideoFrame } from './hooks/useVideoFrame';

    export default function CanvasAnimation() {
        const canvasRef = useRef<HTMLCanvasElement>(null);
        const frame = useVideoFrame(helios);

        useEffect(() => {
            const canvas = canvasRef.current;
            if (!canvas) return;

            const ctx = canvas.getContext('2d');
            if (!ctx) return;

            const { width, height } = canvas;
            const progress = frame / (duration * fps);

            // Clear
            ctx.fillStyle = '#111';
            ctx.fillRect(0, 0, width, height);

            // Draw animated shapes
            const cx = width / 2;
            const cy = height / 2;
            const radius = 100;

            ctx.save();
            ctx.translate(cx, cy);
            ctx.rotate(progress * Math.PI * 2);

            ctx.strokeStyle = '#61dafb';
            ctx.lineWidth = 5;

            // Atom rings
            for (let i = 0; i < 3; i++) {
                ctx.save();
                ctx.rotate((i * Math.PI) / 3);
                ctx.beginPath();
                ctx.ellipse(0, 0, radius, radius / 2.5, 0, 0, Math.PI * 2);
                ctx.stroke();
                ctx.restore();
            }

            // Nucleus
            ctx.fillStyle = '#61dafb';
            ctx.beginPath();
            ctx.arc(0, 0, 20, 0, Math.PI * 2);
            ctx.fill();

            ctx.restore();
        }, [frame]);

        return (
            <canvas
                ref={canvasRef}
                width={1920}
                height={1080}
                style={{ width: '100%', height: '100%' }}
            />
        );
    }
    ```
  </Tab>
</Tabs>

## Animation helpers

Helios provides utility functions for common animation patterns:

```tsx theme={null}
import { interpolate, spring } from '@helios-project/core';
import { useVideoFrame } from './hooks/useVideoFrame';

function HelperDemo() {
    const frame = useVideoFrame(helios);

    // Interpolate x position: 0 -> 200 over frames 0-60
    const x = interpolate(frame, [0, 60], [0, 200], { extrapolateRight: 'clamp' });

    // Spring scale: 0 -> 1 starting at frame 0
    const scale = spring({ 
        frame, 
        fps: 30, 
        from: 0, 
        to: 1, 
        config: { stiffness: 100 } 
    });

    return (
        <div style={{
            transform: `translateX(${x}px) scale(${scale})`,
            width: 100,
            height: 100,
            background: 'hotpink'
        }}>
            Animated
        </div>
    );
}
```

## Sequencing components

### Sequence component

Create time-based sequences that show/hide content:

```tsx components/Sequence.tsx theme={null}
import { useContext } from 'react';
import { sequence } from '@helios-project/core';
import { FrameContext } from './FrameContext';

export const Sequence = ({ from, durationInFrames, children }) => {
    const parentFrame = useContext(FrameContext);
    const { isActive, relativeFrame } = sequence({ 
        frame: parentFrame, 
        from, 
        durationInFrames 
    });

    if (!isActive) return null;

    return (
        <FrameContext.Provider value={relativeFrame}>
            {children}
        </FrameContext.Provider>
    );
};
```

### Series component

Automatically sequence child components one after another:

```tsx components/Series.tsx theme={null}
import { Children, cloneElement, isValidElement } from 'react';

export const Series = ({ children }) => {
    let currentFrom = 0;

    return (
        <>
            {Children.map(children, (child) => {
                if (!isValidElement(child)) {
                    return child;
                }

                const duration = child.props.durationInFrames || 0;
                const newProps = { from: currentFrom };
                currentFrom += duration;

                return cloneElement(child, newProps);
            })}
        </>
    );
};
```

### Frame context

Create a context for sharing frame state:

```tsx components/FrameContext.tsx theme={null}
import { createContext } from 'react';

export const FrameContext = createContext(0);
```

### Usage example

```tsx theme={null}
import { useState, useEffect } from 'react';
import { FrameContext } from './components/FrameContext';
import { Sequence } from './components/Sequence';
import { Series } from './components/Series';

function MovingBox({ color, label }) {
    const frame = useContext(FrameContext);
    const x = frame * 5;

    return (
        <div style={{
            position: 'absolute',
            left: `${x}px`,
            width: 100,
            height: 100,
            backgroundColor: color
        }}>
            {label}
        </div>
    );
}

export default function App() {
    const [frame, setFrame] = useState(0);

    useEffect(() => {
        return helios.subscribe((state) => setFrame(state.currentFrame));
    }, []);

    return (
        <FrameContext.Provider value={frame}>
            <Series>
                <Sequence durationInFrames={60}>
                    <MovingBox color="#ff4444" label="Seq 1" />
                </Sequence>

                <Sequence durationInFrames={60}>
                    <MovingBox color="#4444ff" label="Seq 2" />
                </Sequence>
            </Series>
        </FrameContext.Provider>
    );
}
```

## Best practices

<AccordionGroup>
  <Accordion title="Initialize Helios outside components">
    Create the Helios instance outside your component to avoid recreating it on every render:

    ```tsx theme={null}
    // Good: Single instance
    const helios = new Helios({ duration: 5, fps: 30 });
    helios.bindToDocumentTimeline();

    export default function App() {
        const frame = useVideoFrame(helios);
        // ...
    }
    ```

    ```tsx theme={null}
    // Avoid: New instance on every render
    export default function App() {
        const helios = new Helios({ duration: 5, fps: 30 }); // ❌
        // ...
    }
    ```
  </Accordion>

  <Accordion title="Bind to document timeline">
    Always call `bindToDocumentTimeline()` to enable external control by the player or renderer:

    ```tsx theme={null}
    const helios = new Helios({ duration: 5, fps: 30 });
    helios.bindToDocumentTimeline();
    ```
  </Accordion>

  <Accordion title="Expose helios for debugging">
    Attach the Helios instance to `window` for easy debugging and player integration:

    ```tsx theme={null}
    if (typeof window !== 'undefined') {
        (window as any).helios = helios;
    }
    ```
  </Accordion>

  <Accordion title="Use strict mode carefully">
    React 18+ Strict Mode causes double-mounting in development. This is fine for Helios, but be aware of potential duplicate subscriptions during development.
  </Accordion>
</AccordionGroup>

## TypeScript support

Helios is written in TypeScript and provides full type definitions:

```tsx theme={null}
import type { Helios } from '@helios-project/core';

interface VideoFrameHookReturn {
    frame: number;
    progress: number;
}

export function useVideoFrame(helios: Helios): number {
    const [frame, setFrame] = useState<number>(helios.getState().currentFrame);
    // ...
    return frame;
}
```

## Next steps

<CardGroup cols={2}>
  <Card title="Animation helpers" icon="wand-magic-sparkles" href="/features/animation-helpers">
    Learn about interpolation, spring physics, and easing functions
  </Card>

  <Card title="Canvas rendering" icon="paintbrush" href="/guides/canvas">
    Create high-performance canvas animations
  </Card>

  <Card title="Sequences" icon="film" href="/guides/sequences">
    Build complex multi-scene animations
  </Card>

  <Card title="Export videos" icon="file-export" href="/guides/export">
    Render your animations to video files
  </Card>
</CardGroup>
