Leva Documentation

repository·main·Indexed 24 days ago

https://github.com/pmndrs/leva

A customizable and extensible GUI for developers to create control panels. Leva features smart input type recognition and provides a headless mode for managing state and metadata without the default HTML UI via hooks like useControls, useLevaInputs, and useLevaInput. It supports a variety of input types including NUMBER, COLOR, VECTOR2D/3D, and specialized helpers like folder, button, and monitor. The ecosystem includes plugins for Bezier curves (@leva-ui/plugin-bezier), mathematical plotting (@leva-ui/plugin-plot), and spring physics (@leva-ui/plugin-spring).

Tokens
26.4K
Snippets
89
Records
160
Agent score
91%

What's inside Leva

  1. Install and use the Spring Plugin

    main

    The Spring Plugin provides an editor for spring physics configurations, allowing you to adjust tension, friction, and mass.

    npm i @leva-ui/plugin-spring
    import { useControls } from 'leva'
    import { spring } from '@leva-ui/plugin-spring'
    
    function MyComponent() {
      const { mySpring } = useControls({
        mySpring: spring({
          tension: 100,
          friction: 30,
          mass: 1,
        }),
      })
    
      // Returns a spring configuration object
      return <div>{mySpring.toString()}</div>
    }
  2. Handle Optional, Disabled, and Transient Inputs

    main

    Inputs configured with optional: true or disabled: true will have their return type include undefined.

    By default, inputs with an onChange handler are considered 'transient' and are not returned in the useControls object. To ensure the value is returned while still using onChange, set transient: false.

    // Optional/Disabled return undefined
    const { optionalValue } = useControls({
      optionalValue: {
        value: 'hello',
        optional: true,
      },
    })
    // optionalValue: string | undefined
    
    // Transient vs Non-transient
    const { color } = useControls({
      color: {
        value: '#f00',
        onChange: (v) => console.log(v),
        transient: false, // Set to false to ensure 'color' is returned in the object
      },
    })
    // color: string
  3. Disable the Leva GUI

    main

    To completely hide the GUI (for example, based on user preferences), set the hidden prop to true on the <Leva> component. Note that useControls hooks will still function, but their UI will not be rendered.

    import { Leva } from 'leva'
    
    function MyApp() {
      return (
        <>
          <Leva hidden={true} />
        </>
      )
        </>
      )
    }
  4. Quick Start with Leva Headless

    main

    Leva Headless allows you to manage state and access input metadata to build custom UIs (e.g., for WebXR, React Three Fiber, or custom component libraries).

    Use useControls to manage state and useLevaInputs to retrieve metadata for rendering your own controls.

    import { useControls, useLevaInputs } from 'leva/headless'
    
    function MyComponent() {
      // useControls works exactly the same - it manages state without rendering UI
      const values = useControls({
        name: 'World',
        count: { value: 0, min: 0, max: 10 },
        color: '#ff0000',
      })
    
      // Get all inputs with metadata to build your custom UI
      const inputs = useLevaInputs()
    
      return (
        <div>
          {/* Your values are still reactive */}
          <p>
            Hello {values.name}! Count: {values.count}
          </p>
    
          {/* Build your own UI using the inputs data */}
          {inputs.map(({ path, input }) => (
            <CustomControl key={path} path={path} input={input} />
          ))}
        </div>
      )
    }
  5. Install and use the Plot Plugin

    main

    The Plot Plugin is a mathematical function plotter and evaluator. It allows you to define an expression and set bounds for the X and Y axes.

    npm i @leva-ui/plugin-plot
    import { useControls } from 'leva'
    import { plot } from '@leva-ui/plugin-plot'
    
    function MyComponent() {
      const { y } = useControls({
        y: plot({
          expression: 'cos(x)',
          graph: true,
          boundsX: [-10, 10],
          boundsY: [0, 100],
        }),
      })
    
      // Evaluate the function at a point
      const result = y(Math.PI)
    
      return <div>cos(π) = {result}</div>
    }
  6. Use TypeScript with useControls

    main

    Leva provides full type inference for control values. You can rely on automatic inference from your schema or explicitly define a schema using the Schema type for better type safety.

    import { useControls, Schema } from 'leva'
    
    // Option 1: Automatic inference
    const { count, name } = useControls({
      count: 0,
      name: 'Hello',
    })
    
    // Option 2: Explicitly typed schema
    type MySchema = {
      count: number
      name: string
      enabled: boolean
    }
    
    const values = useControls<MySchema>({
      count: 0,
      name: 'Hello',
      enabled: true,
    })
  7. Basic usage of useControls and Leva

    main

    To use Leva, import useControls and the Leva component. The Leva component should be rendered once in your application (e.g., at the root) to display the GUI panel. Controls are added to the GUI automatically when the component using useControls is mounted. The order of controls in the GUI follows the order of hook calls.

    import { useControls, Leva } from 'leva'
    
    function MyComponent() {
      const { myValue } = useControls({ myValue: 10 })
      return myValue
    }
    
    function MyApp() {
      return (
        <>
          <Leva />
          <MyComponent />
        </>
      )
    }
  8. Type Folders and Input Options

    main

    Folders and complex input options (like min/max/step) maintain type safety. For select inputs, use as const to provide better autocomplete for specific string literal types.

    import { folder, useControls } from 'leva'
    
    // Folders
    const values = useControls({
      position: folder({
        x: 0,
        y: 0,
      }),
    })
    // values.position.x is typed as number
    
    // Select inputs with autocomplete
    type Preset = 'low' | 'medium' | 'high'
    
    const { preset } = useControls({
      preset: {
        value: 'medium' as Preset,
        options: ['low', 'medium', 'high'] as const,
      },
    })
  9. Create multiple Leva panels with separate stores

    main

    You can isolate different parts of your application's configuration by creating multiple stores using useCreateStore. Each store can then be rendered in its own LevaPanel.

    1. Create stores using useCreateStore().
    2. Pass the store to useControls via the options object: { store: myStore }.
    3. Render a LevaPanel for each store, optionally providing a titleBar configuration.
    import { useCreateStore, useControls, LevaPanel } from 'leva'
    
    function MyApp() {
      const uiStore = useCreateStore()
      const sceneStore = useCreateStore()
    
      const uiValues = useControls(
        {
          showUI: true,
          theme: 'dark',
        },
        { store: uiStore }
      )
    
      const sceneValues = useControls(
        {
          cameraPosition: [0, 0, 5],
          lightIntensity: 1,
        },
        { store: sceneStore }
      )
    
      return (
        <>
          <LevaPanel store={uiStore} titleBar={{ title: 'UI Settings' }} />
          <LevaPanel store={sceneStore} titleBar={{ title: 'Scene Settings' }} />
        </>
      )
    }