For AI agents: the complete documentation index is available at /toi/llms.txt, the full documentation bundle is available at /toi/llms-full.txt, and this page is available as Markdown at /toi/guide/basic/additional-props.md.
  • English
  • Additional Props

    ToiProps<Response> only describes ref and resolve. A component almost always needs more than that — a message to display, an item to confirm deletion of, and so on. Define those as part of the component's own props, then pass them as a second argument to toi:

    import { toi, ToiHost } from '@praha/toi';
    
    import type { ToiProps } from '@praha/toi';
    import type { FC } from 'react';
    
    type ConfirmProps = ToiProps<boolean> & { message: string };
    
    const Confirm: FC<ConfirmProps> = ({ ref, resolve, message }) => (
      <dialog ref={ref} open>
        <p>{message}</p>
        <button onClick={() => resolve(true)}>OK</button>
        <button onClick={() => resolve(false)}>Cancel</button>
      </dialog>
    );
    
    export default function App() {
      return (
        <>
          <button onClick={() => toi(Confirm, { message: 'Are you sure?' })}>
            Delete
          </button>
          <ToiHost />
        </>
      );
    }

    toi infers both the response type and the extra props from Confirm's own props type — there's nothing to annotate on the call to toi itself.

    Optional vs. required

    Whether the second argument to toi (or to a function returned by toi.fn) can be omitted is inferred from the extra props themselves: if every extra prop is optional, the argument is too; if any is required, the argument is required.

    type ConfirmProps = ToiProps<boolean> & { message?: string };
    
    const Confirm: FC<ConfirmProps> = ({ ref, resolve, message = 'Are you sure?' }) => (/* ... */);
    
    await toi(Confirm); // fine — `message` is optional
    await toi(Confirm, { message: 'Really?' }); // also fine
    type ConfirmProps = ToiProps<boolean> & { message: string };
    
    const Confirm: FC<ConfirmProps> = ({ ref, resolve, message }) => (/* ... */);
    
    await toi(Confirm); // type error — `message` is required
    await toi(Confirm, { message: 'Are you sure?' }); // fine

    Inline components

    When passing a component defined inline, its props usually can't be inferred well enough on their own — annotate Response (and Props, if it needs anything beyond ToiProps) explicitly:

    import { toi, ToiHost } from '@praha/toi';
    
    import type { ToiProps } from '@praha/toi';
    
    type ConfirmProps = ToiProps<boolean> & { message: string };
    
    export default function App() {
      return (
        <>
          <button
            onClick={() => toi<boolean, ConfirmProps>(({ ref, resolve, message }) => (
              <dialog ref={ref} open>
                <p>{message}</p>
                <button onClick={() => resolve(true)}>OK</button>
                <button onClick={() => resolve(false)}>Cancel</button>
              </dialog>
            ), { message: 'Are you sure?' })}
          >
            Delete
          </button>
          <ToiHost />
        </>
      );
    }