---
title: "Input Parameters"
description: "Pass initialization data to actors when creating instances"
skill: true
---

Actors can receive input parameters when created, allowing for flexible initialization and configuration. Input is passed during actor creation and is available in lifecycle hooks.

## Passing Input to Actors

Input is provided when creating actor instances using the `input` property:

```typescript
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

interface GameInput {
  gameMode: string;
  maxPlayers: number;
  difficulty?: string;
}

const game = actor({
  state: { gameMode: "", maxPlayers: 0, difficulty: "medium" },
  createState: (c, input: GameInput) => ({
    gameMode: input.gameMode,
    maxPlayers: input.maxPlayers,
    difficulty: input.difficulty ?? "medium",
  }),
  actions: {}
});

const registry = setup({ use: { game } });
const client = createClient<typeof registry>("http://localhost:6420");

// Client side - create with input
const gameHandle = await client.game.create(["game-123"], {
  input: {
    gameMode: "tournament",
    maxPlayers: 8,
    difficulty: "hard",
  }
});

// getOrCreate can also accept input (used only if creating)
const gameHandle2 = client.game.getOrCreate(["game-456"], {
  createWithInput: {
    gameMode: "casual",
    maxPlayers: 4,
  }
});
```

## Accessing Input in Lifecycle Hooks

Input is available in lifecycle hooks via the `opts.input` parameter:

```typescript
import { actor } from "rivetkit";

interface ChatRoomInput {
  roomName: string;
  isPrivate: boolean;
  maxUsers?: number;
}

interface ChatRoomState {
  name: string;
  isPrivate: boolean;
  maxUsers: number;
  users: Record<string, boolean>;
  messages: string[];
}

// Mock function for demonstration
function setupPrivateRoomLogging(roomName: string) {
  console.log(`Setting up logging for private room: ${roomName}`);
}

const chatRoom = actor({
  state: { name: "", isPrivate: false, maxUsers: 50, users: {}, messages: [] } as ChatRoomState,
  createState: (c, input: ChatRoomInput): ChatRoomState => ({
    name: input?.roomName ?? "Unnamed Room",
    isPrivate: input?.isPrivate ?? false,
    maxUsers: input?.maxUsers ?? 50,
    users: {},
    messages: [],
  }),

  onCreate: (c, input: ChatRoomInput) => {
    console.log(`Creating room: ${input.roomName}`);

    // Setup external services based on input
    if (input.isPrivate) {
      setupPrivateRoomLogging(input.roomName);
    }
  },

  actions: {
    // Input remains accessible in actions via initial state
    getRoomInfo: (c) => ({
      name: c.state.name,
      isPrivate: c.state.isPrivate,
      maxUsers: c.state.maxUsers,
      currentUsers: Object.keys(c.state.users).length,
    }),
  },
});
```

## Input Validation

You can validate input parameters in the `createState` or `onCreate` hooks:

```typescript
import { actor } from "rivetkit";
import { z } from "zod";

const GameInputSchema = z.object({
  gameMode: z.enum(["casual", "tournament", "ranked"]),
  maxPlayers: z.number().min(2).max(16),
  difficulty: z.enum(["easy", "medium", "hard"]).optional(),
});

type GameInput = z.infer<typeof GameInputSchema>;

interface GameState {
  gameMode: string;
  maxPlayers: number;
  difficulty: string;
  players: Record<string, boolean>;
  gameState: string;
}

const game = actor({
  state: { gameMode: "", maxPlayers: 0, difficulty: "medium", players: {}, gameState: "waiting" } as GameState,
  createState: (c, inputRaw: GameInput): GameState => {
    // Validate input
    const input = GameInputSchema.parse(inputRaw);

    return {
      gameMode: input.gameMode,
      maxPlayers: input.maxPlayers,
      difficulty: input.difficulty ?? "medium",
      players: {},
      gameState: "waiting",
    };
  },

  actions: {
    // Actions can access the validated input via state
    getGameInfo: (c) => ({
      gameMode: c.state.gameMode,
      maxPlayers: c.state.maxPlayers,
      difficulty: c.state.difficulty,
      currentPlayers: Object.keys(c.state.players).length,
    }),
  },
});
```

## Input vs Connection Parameters

Input parameters are different from connection parameters:

- **Input**:
  - Passed when creating the actor instance
  - Use for actor-wide configuration
  - Available in lifecycle hooks
- **Connection parameters**:
  - Passed when connecting to an existing actor
  - Used for connection-specific configuration
  - Available in connection hooks

```typescript
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

interface RoomInput { roomName: string; isPrivate: boolean; }

const chatRoom = actor({
  state: { name: "", isPrivate: false },
  createState: (c, input: RoomInput) => ({ name: input.roomName, isPrivate: input.isPrivate }),
  connState: { userId: "", displayName: "" },
  createConnState: (c, params: { userId: string; displayName: string }) => ({
    userId: params.userId,
    displayName: params.displayName,
  }),
  actions: {}
});

const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>("http://localhost:6420");

// Actor creation with input
const room = await client.chatRoom.create(["room-123"], {
  input: {
    roomName: "General Discussion",
    isPrivate: false,
  },
});
```

## Input Best Practices

### Use Type Safety

Define input types to ensure type safety:

```typescript
import { actor } from "rivetkit";

interface GameInput {
  gameMode: "casual" | "tournament" | "ranked";
  maxPlayers: number;
  difficulty?: "easy" | "medium" | "hard";
}

interface GameState {
  gameMode: string;
  maxPlayers: number;
  difficulty: string;
}

const game = actor({
  state: { gameMode: "", maxPlayers: 0, difficulty: "medium" } as GameState,
  createState: (c, input: GameInput): GameState => ({
    gameMode: input.gameMode,
    maxPlayers: input.maxPlayers,
    difficulty: input.difficulty ?? "medium",
  }),

  actions: {
    // Actions are now type-safe
  },
});
```

### Store Input in State

Input is only available in `createState` and `onCreate` lifecycle hooks. If you need to access input data later (in actions, timers, or other hooks), store it in the actor's state during creation. This is the recommended pattern because input shapes can evolve over time, and persisting input in state ensures you always have access to the values the actor was created with:

```typescript
import { actor } from "rivetkit";

interface GameInput {
  gameMode: string;
  maxPlayers: number;
  difficulty?: string;
}

interface GameConfig {
  gameMode: string;
  maxPlayers: number;
  difficulty: string;
}

interface GameState {
  config: GameConfig;
  players: Record<string, boolean>;
  gameState: string;
}

const game = actor({
  state: {
    config: { gameMode: "", maxPlayers: 0, difficulty: "medium" },
    players: {},
    gameState: "waiting"
  } as GameState,
  createState: (c, input: GameInput): GameState => ({
    // Store input configuration in state
    config: {
      gameMode: input.gameMode,
      maxPlayers: input.maxPlayers,
      difficulty: input?.difficulty ?? "medium",
    },
    // Runtime state
    players: {},
    gameState: "waiting",
  }),

  actions: {
    getConfig: (c) => c.state.config,
    updateDifficulty: (c, difficulty: string) => {
      c.state.config.difficulty = difficulty;
    },
  },
});
```

## API Reference

- [`CreateOptions`](/typedoc/interfaces/rivetkit.client_mod.CreateOptions.html) - Options for creating actors
- [`CreateRequest`](/typedoc/types/rivetkit.client_mod.CreateRequest.html) - Request type for creation
- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining input types
