Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default tseslint.config(
'./crates/bindings-typescript/test-app/tsconfig.json',
'./templates/react-ts/tsconfig.json',
'./templates/chat-react-ts/tsconfig.json',
'./templates/hangman-react-ts/tsconfig.json',
'./templates/basic-ts/tsconfig.json',
'./templates/angular-ts/tsconfig.app.json',
'./docs/tsconfig.json',
Expand Down
49 changes: 49 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ packages:
- 'crates/bindings-typescript/test-app'
- 'crates/bindings-typescript/case-conversion-test-client'
- 'templates/chat-react-ts'
- 'templates/hangman-react-ts'
- 'templates/react-ts'
- 'templates/basic-ts'
- 'templates/vue-ts'
Expand Down
7 changes: 7 additions & 0 deletions templates/hangman-react-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
dist
*.log

.DS_Store

spacetime.local.json
21 changes: 21 additions & 0 deletions templates/hangman-react-ts/.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"description": "Competitive Hangman game with React and TypeScript server",
"client_framework": "React",
"client_lang": "typescript",
"server_lang": "typescript",
"tags": ["Launchpad"],
"builtWith": [
"react",
"react-dom",
"eslint",
"vitejs",
"eslint-plugin-react-hooks",
"eslint-plugin-react-refresh",
"globals",
"prettier",
"typescript",
"typescript-eslint",
"vite",
"spacetimedb"
]
}
118 changes: 118 additions & 0 deletions templates/hangman-react-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
Get a competitive SpacetimeDB Hangman game running with React and TypeScript.

## Prerequisites

- [Node.js](https://nodejs.org/) 18+ installed
- [SpacetimeDB CLI](https://spacetimedb.com/install) installed

Install the [SpacetimeDB CLI](https://spacetimedb.com/install) before continuing.

---

## Create your project

Run the `spacetime dev` command to create a new project with a TypeScript SpacetimeDB module and React client.

This will start the local SpacetimeDB server, publish your module, generate TypeScript bindings, and start the React development server.

```bash
spacetime dev --template hangman-react-ts
```

## Open your app

Navigate to [http://localhost:5173](http://localhost:5173) to play Hangman.

Every player guesses the same hidden word on a private board. A round lasts 60 seconds, followed by 10 seconds of revealed results and rankings.

## Explore the project structure

Your project contains both server and client code.

Edit `spacetimedb/src/index.ts` to change game rules or the word list. Edit `src/App.tsx` to change the game interface.

```
my-spacetime-app/
├── spacetimedb/ # Your SpacetimeDB module
│ └── src/
│ └── index.ts # Game tables, reducers, and round transitions
├── src/ # React frontend
│ ├── App.tsx
│ └── module_bindings/ # Auto-generated types
└── package.json
```

## Understand tables and reducers

Open `spacetimedb/src/index.ts` to see the module code. The template includes public round and result tables, private player progress, and scheduled round transitions. The `set_name` reducer registers a nickname and `guess_letter` submits one letter for the current round.

Tables store game state. Reducers are functions that modify data - they are the only way to write to the database.

```typescript
export const set_name = spacetimedb.reducer(
{ name: t.string() },
(ctx, { name }) => {
const trimmedName = name.trim();
if (trimmedName.length === 0 || trimmedName.length > 20) {
throw new SenderError('Names must be between 1 and 20 characters');
}

const existing = ctx.db.player.identity.find(ctx.sender);
if (existing) {
ctx.db.player.identity.update({ ...existing, name: trimmedName });
} else {
ctx.db.player.insert({ identity: ctx.sender, name: trimmedName });
}
}
);

export const guess_letter = spacetimedb.reducer(
{ letter: t.string() },
(ctx, { letter }) => {
const guess = letter.trim().toUpperCase();
if (!/^[A-Z]$/.test(guess)) {
throw new SenderError('Guess one letter from A to Z');
}

// Update this player's private progress for the active round.
}
);
```

## Test with the CLI

Open a new terminal and navigate to your project directory. Then use the SpacetimeDB CLI to join a round, guess letters, and inspect your state.

```bash
cd my-spacetime-app

# Pick a nickname before guessing
spacetime call set_name '"Ada"'

# Guess one letter
spacetime call guess_letter '"A"'

# Inspect the active public round and your private board view
spacetime sql "SELECT * FROM current_round"
spacetime sql "SELECT * FROM my_progress"
```

## Understand round state and privacy

The module runs one shared competitive round at a time:

- `current_round` publishes the timer, difficulty, and word length. It reveals the answer only during results.
- `my_progress` exposes each player only to their own masked word and guesses during an active round.
- `round_result` publishes the completed round standings once the timer ends.
- `transition_timer` schedules the results phase and the start of the next round.

## Customize the game

The built-in word list and round durations are at the top of `spacetimedb/src/index.ts`. Add words, adjust difficulty labels, or change the active and results durations there.

The React UI in `src/App.tsx` includes the gallows drawing, masked-word board, keyboard, timer, nickname form, and standings panel. Edit those components and `src/App.css` to change how the game looks and plays.

## Next steps

- Read the [TypeScript SDK Reference](https://spacetimedb.com/docs/intro/core-concepts/clients/typescript-reference) for detailed API docs
- See the [Chat App Tutorial](https://spacetimedb.com/docs/intro/tutorials/chat-app) for another complete React example
12 changes: 12 additions & 0 deletions templates/hangman-react-ts/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SpacetimeDB Hangman</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
36 changes: 36 additions & 0 deletions templates/hangman-react-ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@clockworklabs/hangman-react-ts",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"format": "prettier . --write --ignore-path ../../.prettierignore",
"lint": "eslint . && prettier . --check --ignore-path ../../.prettierignore",
"preview": "vite preview",
"generate": "cargo run -p gen-bindings -- --out-dir src/module_bindings --module-path spacetimedb && prettier --write src/module_bindings",
"spacetime:generate": "spacetime generate --lang typescript --out-dir src/module_bindings --module-path spacetimedb",
"spacetime:publish:local": "spacetime publish --module-path spacetimedb --server local",
"spacetime:publish": "spacetime publish --module-path spacetimedb --server maincloud"
},
"dependencies": {
"spacetimedb": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^5.0.2",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"prettier": "^3.3.3",
"typescript": "~5.6.2",
"typescript-eslint": "^8.18.2",
"vite": "^7.1.5"
}
}
18 changes: 18 additions & 0 deletions templates/hangman-react-ts/spacetimedb/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "spacetime-module",
"version": "1.0.0",
"description": "",
"scripts": {
"build": "spacetime build",
"publish": "spacetime publish"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"spacetimedb": "workspace:*"
},
"devDependencies": {
"typescript": "~5.6.2"
}
}
Loading
Loading