website/src/content/docs/actors/ephemeral-variables.mdx
vars is designed to complement state, not replace it. Most actors should use both: state for critical business data and vars for ephemeral or non-serializable data.
There are two ways to define an actor's initial vars:
<Tabs> <Tab title="Static Initial Variables">Define an actor vars as a constant value:
import { actor } from "rivetkit";
// Mock event emitter for demonstration
interface EventEmitter {
on: (event: string, callback: (data: unknown) => void) => void;
emit: (event: string, data: unknown) => void;
}
function createEventEmitter(): EventEmitter {
const listeners: Record<string, ((data: unknown) => void)[]> = {};
return {
on: (event, callback) => {
listeners[event] = listeners[event] || [];
listeners[event].push(callback);
},
emit: (event, data) => {
listeners[event]?.forEach(cb => cb(data));
}
};
}
// Define vars as a constant
const counter = actor({
state: { count: 0 },
// Define ephemeral variables
vars: {
lastAccessTime: 0,
emitter: createEventEmitter()
},
actions: {
increment: (c) => ++c.state.count
}
});
This value will be cloned for every new actor using structuredClone.
Create actor state dynamically on each actors' start:
import { actor } from "rivetkit";
// Mock event emitter for demonstration
interface EventEmitter {
on: (event: string, callback: (data: unknown) => void) => void;
emit: (event: string, data: unknown) => void;
}
function createEventEmitter(): EventEmitter {
const listeners: Record<string, ((data: unknown) => void)[]> = {};
return {
on: (event, callback) => {
listeners[event] = listeners[event] || [];
listeners[event].push(callback);
},
emit: (event, data) => {
listeners[event]?.forEach(cb => cb(data));
}
};
}
// Define vars with initialization logic
const counter = actor({
state: { count: 0 },
// Define vars using a creation function
createVars: () => {
return {
lastAccessTime: Date.now(),
emitter: createEventEmitter()
};
},
actions: {
increment: (c) => ++c.state.count
}
});
Otherwise, the return type will not be inferred and c.vars will be of type unknown.
</Note>
Vars can be accessed and modified through the context object with c.vars:
import { actor } from "rivetkit";
// Mock event emitter for demonstration
interface EventEmitter {
on: (event: string, callback: (data: number) => void) => void;
emit: (event: string, data: number) => void;
}
function createEventEmitter(): EventEmitter {
const listeners: Record<string, ((data: number) => void)[]> = {};
return {
on: (event, callback) => {
listeners[event] = listeners[event] || [];
listeners[event].push(callback);
},
emit: (event, data) => {
listeners[event]?.forEach(cb => cb(data));
}
};
}
const counter = actor({
// Persistent state - saved to storage
state: { count: 0 },
// Create ephemeral objects that won't be serialized
createVars: () => {
// Create an event emitter (can't be serialized)
const emitter = createEventEmitter();
// Set up event listener directly in createVars
emitter.on('count-changed', (newCount) => {
console.log(`Count changed to: ${newCount}`);
});
return { emitter };
},
actions: {
increment: (c) => {
// Update persistent state
c.state.count += 1;
// Use non-serializable emitter
c.vars.emitter.emit('count-changed', c.state.count);
return c.state.count;
}
}
});
vars vs stateIn practice, most actors will use both: state for critical business data and vars for ephemeral or non-serializable data.
Use vars when:
Use state when: