Claude Code Plugins

Community-maintained marketplace

Feedback

Write Roblox games in TypeScript. Use for roblox-ts setup, configuration, type-safe Roblox API usage, project structure, package management, compiling TypeScript to Luau, debugging, and working with roblox-ts packages and best practices.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name roblox-ts
description Write Roblox games in TypeScript. Use for roblox-ts setup, configuration, type-safe Roblox API usage, project structure, package management, compiling TypeScript to Luau, debugging, and working with roblox-ts packages and best practices.
version 1.0.0

Roblox-TS - Claude Code Skill

A comprehensive skill for developing Roblox games with TypeScript using roblox-ts.

Overview

Roblox-TS is a TypeScript-to-Luau compiler that allows you to write Roblox games using TypeScript's type safety, modern syntax, and tooling while compiling to performant Luau code.

What This Skill Covers:

  • Setup & Configuration: Project initialization, tsconfig.json, Rojo integration
  • Type-Safe Roblox API: Working with Roblox services, instances, and datatypes
  • TypeScript Features: Using modern TypeScript with Roblox limitations
  • Package Management: Installing and using @rbxts packages from npm
  • Build & Deployment: Compilation, optimization, and publishing workflow
  • Best Practices: Type safety, performance, project structure, and debugging

Documentation Sources:

  • Official roblox-ts docs (https://roblox-ts.com/)
  • TypeScript handbook (adapted for Roblox)
  • Roblox Creator documentation

Quick Start

Installation

# Install roblox-ts globally
npm install -g roblox-ts

# Create new project
npx rbxtsc init

# Or initialize in existing directory
cd my-game
npx rbxtsc init

# Install packages
npm install

Basic Script Structure

Server Script (src/server/main.server.ts):

import { Players, Workspace } from "@rbxts/services";

Players.PlayerAdded.Connect((player) => {
    print(`${player.Name} joined the game!`);
});

Client Script (src/client/main.client.ts):

import { Players, UserInputService } from "@rbxts/services";

const player = Players.LocalPlayer;

UserInputService.InputBegan.Connect((input) => {
    if (input.KeyCode === Enum.KeyCode.Space) {
        print("Space pressed!");
    }
});

Compilation

# Build once
npx rbxtsc

# Watch mode (rebuild on file change)
npx rbxtsc -w

# Build with verbose output
npx rbxtsc -w --verbose

Core Concepts

Type-Safe Roblox API

All Roblox services and APIs are fully typed:

import { Workspace, Players, ReplicatedStorage } from "@rbxts/services";

// Type-safe instance access
const part = Workspace.FindFirstChild("MyPart") as Part | undefined;
if (part) {
    part.BrickColor = BrickColor.Red();
    part.Anchored = true;
}

// Service usage with intellisense
Players.PlayerAdded.Connect((player) => {
    const character = player.Character || player.CharacterAdded.Wait()[0];
    const humanoid = character.FindFirstChildOfClass("Humanoid");
});

TypeScript to Luau Mapping

Arrays:

const array = [1, 2, 3, 4, 5];
array.push(6);
const filtered = array.filter(x => x > 3);

Maps (instead of objects for dictionaries):

// Use Map for key-value pairs
const playerData = new Map<Player, number>();
playerData.set(player, 100);
const health = playerData.get(player);

Sets:

const uniqueIds = new Set<string>();
uniqueIds.add("id1");
uniqueIds.add("id2");

Promises (for async operations):

import { HttpService } from "@rbxts/services";

async function fetchData(url: string) {
    const response = await HttpService.GetAsync(url);
    return response;
}

Roact (React for Roblox)

import Roact from "@rbxts/roact";

interface Props {
    text: string;
    onClick: () => void;
}

function Button({ text, onClick }: Props) {
    return (
        <textbutton
            Size={new UDim2(0, 200, 0, 50)}
            Text={text}
            Event={{ MouseButton1Click: onClick }}
        />
    );
}

When to Use This Skill

  • Setting up new roblox-ts projects
  • Configuring tsconfig.json or Rojo projects
  • Converting Lua code to TypeScript
  • Working with type-safe Roblox APIs
  • Installing and using @rbxts packages
  • Understanding TypeScript/Luau differences
  • Debugging compilation or type errors
  • Structuring roblox-ts projects
  • Using modern TypeScript features with Roblox

Detailed Documentation

This skill uses progressive disclosure for efficient context usage:

  • SKILL.md (this file): Quick reference and common patterns (always loaded)
  • Reference files: Detailed documentation (loaded only when needed)

Available Reference Files:

Usage Tips

This skill activates when you mention roblox-ts concepts:

  • "Set up a new roblox-ts project"
  • "Convert this Lua code to TypeScript"
  • "Use RemoteEvents with type safety"
  • "Install Roact for my roblox-ts game"
  • "Fix roblox-ts compilation error"
  • "Structure my TypeScript Roblox project"

For best results:

  1. Specify if you're working on server or client scripts
  2. Mention specific Roblox services or APIs you're using
  3. Share error messages for compilation issues
  4. Ask about TypeScript equivalents for Lua patterns
  5. Request complete examples for new concepts

Common Patterns

Remote Events with Type Safety

// shared/remotes.ts
import { createRemotes, remote } from "@rbxts/remo";

const remotes = createRemotes({
    playerDamaged: remote<Server, [targetId: string, damage: number]>(),
    updateHealth: remote<Client, [health: number]>(),
});

export = remotes;

// server/combat.server.ts
import remotes from "shared/remotes";

remotes.Server.playerDamaged.connect((player, targetId, damage) => {
    // Type-safe parameters
    print(`${player.Name} damaged ${targetId} for ${damage}`);
});

// Fire to client
remotes.Server.updateHealth.fire(player, 75);

// client/ui.client.ts
import remotes from "shared/remotes";

remotes.Client.updateHealth.connect((health) => {
    print(`Health updated: ${health}`);
});

// Fire to server
remotes.Client.playerDamaged.fire("enemy-123", 25);

Data Stores with Type Safety

import { DataStoreService } from "@rbxts/services";

interface PlayerData {
    coins: number;
    level: number;
    inventory: string[];
}

const dataStore = DataStoreService.GetDataStore("PlayerData");

function savePlayerData(userId: string, data: PlayerData) {
    const success = pcall(() => {
        dataStore.SetAsync(userId, data);
    })[0];
    
    return success;
}

function loadPlayerData(userId: string): PlayerData | undefined {
    const [success, data] = pcall(() => {
        return dataStore.GetAsync(userId) as PlayerData | undefined;
    });
    
    return success ? data : undefined;
}

Character Management

import { Players } from "@rbxts/services";

function setupCharacter(player: Player) {
    const character = player.Character || player.CharacterAdded.Wait()[0];
    const humanoid = character.WaitForChild("Humanoid") as Humanoid;
    const rootPart = character.WaitForChild("HumanoidRootPart") as Part;
    
    humanoid.Died.Connect(() => {
        print(`${player.Name} died`);
    });
    
    return { character, humanoid, rootPart };
}

Players.PlayerAdded.Connect((player) => {
    player.CharacterAdded.Connect(() => {
        setupCharacter(player);
    });
});

GUI with Roact

import Roact from "@rbxts/roact";
import { Players } from "@rbxts/services";

interface State {
    health: number;
}

class HealthBar extends Roact.Component<{}, State> {
    state: State = { health: 100 };
    
    render() {
        return (
            <screengui>
                <frame
                    Size={new UDim2(0, 200, 0, 30)}
                    Position={new UDim2(0.5, -100, 0, 10)}
                    BackgroundColor3={Color3.fromRGB(50, 50, 50)}
                >
                    <frame
                        Size={new UDim2(this.state.health / 100, 0, 1, 0)}
                        BackgroundColor3={Color3.fromRGB(0, 255, 0)}
                    />
                </frame>
            </screengui>
        );
    }
}

const playerGui = Players.LocalPlayer.WaitForChild("PlayerGui");
Roact.mount(<HealthBar />, playerGui);

Configuration Quick Reference

tsconfig.json essentials:

{
  "compilerOptions": {
    "outDir": "out",
    "rootDir": "src",
    "module": "commonjs",
    "strict": true,
    "noLib": true,
    "downlevelIteration": true,
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "types": ["@rbxts/types"]
  }
}

default.project.json (Rojo):

{
  "name": "my-game",
  "tree": {
    "$className": "DataModel",
    "ReplicatedStorage": {
      "$className": "ReplicatedStorage",
      "rbxts_include": {
        "$path": "include",
        "node_modules": { "$path": "node_modules/@rbxts" }
      }
    },
    "ServerScriptService": {
      "$className": "ServerScriptService",
      "TS": { "$path": "out/server" }
    }
  }
}

Best Practices

  1. Use strict type checking - Enable strict: true in tsconfig.json
  2. Prefer Maps over objects - For dictionaries with dynamic keys
  3. Use proper exports - export = for modules, named exports for utilities
  4. Leverage type guards - Use typeIs and classIs for runtime checks
  5. Structure by feature - Group related server/client/shared code
  6. Use @rbxts packages - Don't reinvent the wheel (Roact, t, remo, etc.)
  7. Handle promises properly - Always catch rejections
  8. Avoid any type - Use unknown and type guards instead

TypeScript vs Lua Key Differences

Arrays start at 0 (not 1):

const arr = [1, 2, 3];
print(arr[0]); // 1 (TypeScript)
// In Lua: arr[1] is 1

Use === for equality:

if (value === undefined) { }  // TypeScript
// Not: if value == nil then (Lua)

No self keyword:

class MyClass {
    value = 10;
    
    method() {
        print(this.value);  // TypeScript uses 'this'
    }
}

Destructuring:

const { Name, Health } = player;
const [first, second] = array;

Troubleshooting

"Cannot find module '@rbxts/services'" - Run npm install @rbxts/services @rbxts/types

Compilation errors - Check tsconfig.json has correct settings, ensure TypeScript version is compatible

Type errors with Roblox API - Update @rbxts/types package for latest API types

"Expected 'this' in method" - Use arrow functions for callbacks: () => this.method()

Import errors - Use correct export style (export = for modules, export default not supported)

For detailed troubleshooting, see BUILD.md and BEST_PRACTICES.md reference files.

Skill Coverage

Complete roblox-ts Feature Set: ✅ Project setup and configuration
✅ TypeScript compilation to Luau
✅ Full Roblox API type definitions
✅ Package management (@rbxts ecosystem)
✅ Roact/React for UI
✅ Remote communication patterns
✅ Data storage patterns
✅ Build and deployment workflow
✅ Migration from Lua
✅ Performance optimization

Best Practices Emphasized:

  • Strict type safety for reliability
  • Map/Set over objects for collections
  • Proper async handling with Promises
  • Feature-based project structure
  • Leveraging @rbxts package ecosystem
  • Type guards for runtime safety