Documentation
📦 Crate
About

Crate

Crate

A simple to use, scalable state container built for the roblox-ts ecosystem.

User Crate
new Crate<User>({
    name: "Neohertz",
 
    character: {
        health: 0,
        walkSpeed: 0,
    }
 
    stats: {
        kills: 0,
        deaths: 0,
    }
})
⚠️

Please note that Crate.ts is still in early development. The API is not final, and is subject to breaking changes. You may encounter bugs! Use at your own risk.

Features

💫 Magical Mutation

Crate's update method allows you to update deeply nested partial tables easily.

Mutation (using above crate)
// Kill the player and increment their deaths.
user.update({
    character: {
        health: 0 // Set health to 0
    },
    stats: {
        deaths: (v) => v + 1 // Increment Deaths
    }
})

⚡️ Lightning Fast Access

Using a selector pattern inspired by reflex, accessing data from crates is fast and easy as pie.

Access (using above crate)
const userName = user.getState(
    (state) => state.name,
)
 
print(userName) // "ztrehoeN"

👀 Smart Listeners

Use crate's onUpdate() method to listen to specific parts of your store.

Listeners (using above crate)
// Listen to state updates to a specific member (deaths)
user.onUpdate(
    (state) => state.stats.deaths,
    (deaths) => print(deaths) // 1
)

Crates also provide a listener that receives an update describing the changes made between state updates. This allows you to easily replicate the state, validate data, and more!

Diff Retrieval (using above crate)
user.useDiff((diff) => {
    print(diff) // { name: "Neohertz" }
})
 
user.update({
    name: (v) => v.reverse(), // included, state does change.
    stats: {
        kills: 0, // doesn't get included since it's equal.
    }
})

🪄 Side Effects

Mutate incoming state updates with middleware.

Side Effects (using above crate)
// Ensure the player's health is never less than 0.
user.useMiddleware("character", (oldValue, newValue) => {
    const { health, walkSpeed } = newValue;
 
    return {
        health: math.clamp(health, 0, math.huge),
        walkSpeed,
    }
})
 
// Deal a ridiculous amount of damage to the player.
user.update({
    character: { health: (hp) => hp - 10000 }
})
 
// Retrieve final health value.
const hp = user.getState((s) => s.character.health)
print(hp) // 0 - was clamped by middleware.

⚛️ React Integration

Using the optional @rbxts/react-crate (opens in a new tab) package, you can easily listen and memoize state updates in react components.

React Integration
const crate = new Crate<ClientState>({
	clicks: 0,
});
 
export function ButtonComponent() {
	const clicks = useCrate(crate, state => state.clicks);
 
	return (
		<textbutton
			Event={{
				MouseButton1Click: () => {
					crate.update({ clicks: v => v + 1 });
				},
			}}
			Size={UDim2.fromOffset(200, 50)}
			Text={`Clicks: ${clicks}`}
		/>
	);
}

Think you're ready to give crate a test drive? Get Started →