Skip to main content
RapidDev - Software Development Agency
V0 TemplatesGameIntermediate to customize

Tetris V0 Template: The Falling-Blocks Playbook for React Developers

The Tetris v0 template is a complete browser falling-blocks game built with Next.js, HTML5 Canvas, and useReducer. It ships with all 7 standard tetrominoes, ghost piece rendering, line clearing with Tetris scoring, and a level progression system. Fork it, ship it to Vercel, and use the prompt pack to add touch controls, a hold piece, localStorage high scores, or a Supabase leaderboard.

GameIntermediate~5 minutes

Best for

Developers who want a complete browser Tetris implementation to study, extend, or ship as a portfolio piece

Stack

Next.js 14+TypeScriptTailwind CSSHTML5 CanvasReact useReducer

A ready-made Tetris UI you can fork, run, and customize with the prompt pack below.

What's actually inside

The honest engineer's breakdown — what the Tetristemplate does, how it's wired, and where it's opinionated.

This template is one of the most architecturally complete game implementations available on v0.dev. TetrisCanvas is a useRef'd HTML5 Canvas element that handles all rendering each frame: the locked board cells, the active tetromino in its current color, the faded GhostPiece showing where the piece will land, and the grid lines. All rendering is pure Canvas 2D — no DOM elements, no CSS animations, which is exactly what a fast game loop requires.

GameStateReducer is the core of the architecture. It manages the entire game state — board (a 2D array of color strings), currentPiece, nextPiece, score, level, and lines cleared — via dispatched actions: TICK, MOVE_LEFT, MOVE_RIGHT, ROTATE, HARD_DROP, GAME_OVER. The GameLoop drives this with a setInterval that fires TICK actions at decreasing intervals as level increases (standard Tetris speed curve). CollisionDetector validates every movement against both the 10×20 board boundaries and the locked cells. LineClearing scans full rows after each lock, removes them, shifts the board down, and calculates points using the classic Tetris scoring formula (1 line = 100 pts, 2 = 300, 3 = 500, 4 = 800).

Honest caveats: the rotation logic doesn't implement the Super Rotation System wall kicks — pieces at the edges may get stuck or clip through walls during rotation (the wall-kick gotcha below has the fix). There's also no mobile touch support in the base template; the game is unplayable on phones without the touch controls prompt. And setInterval in React StrictMode creates a well-known double-interval bug in development — the fix is in the gotchas section.

Key UI components

TetrisCanvas

HTML5 Canvas rendering play grid, active tetromino, ghost piece, and score overlay each frame

TetrominoEngine

Defines the 7 standard tetrominoes (I, O, T, S, Z, J, L) as matrix arrays with all rotation states

GameLoop

setInterval-based drop timer that speeds up each level, dispatching TICK to GameStateReducer

CollisionDetector

Validates tetromino movement against board boundaries and locked cells before applying state changes

LineClearing

Scans for full rows after each lock, removes them, shifts board down, and calculates Tetris scoring

GameStateReducer

useReducer managing board, currentPiece, nextPiece, score, level, and lines cleared

NextPiecePreview

Small canvas or div grid displaying the upcoming tetromino

GhostPiece

Renders a faded preview of where the current tetromino will land at the bottom

Libraries it leans on

HTML5 Canvas 2D

Renders the entire game board, tetrominoes, ghost piece, and score text each animation frame

React useReducer

shadcn/ui

Dialog for game-over overlay, Button components for mobile touch controls prompt

Fork it and get it running

This template runs entirely client-side — no environment variables needed for the base game. Fork, preview, and publish to Vercel in about 5 minutes.

1

Fork the template on v0.dev

Open https://v0.dev/chat/community/E7PZvkE1Rf1 in your browser and click the 'Fork' button in the top-right area of the community page. This copies the complete template — TetrisCanvas, TetrominoEngine, GameStateReducer, and all supporting files — into your own V0 project. No credits are consumed by forking.

Tip: Forking is instant; the game code is immediately available in the V0 editor.

You should see: The V0 editor opens with the Tetris project loaded and the game visible in the Preview tab.

2

Play the game in Preview to verify it works

Click the Preview tab in the V0 editor. The game should start with an idle screen. Press Space or click to start. Use arrow keys: Left/Right to move the tetromino, Up to rotate, Down for soft drop, Space for hard drop. Watch for line clears and score updates. Confirm the GhostPiece (faded preview) appears at the bottom of the column.

Tip: If pieces fall at double speed, that's the StrictMode setInterval bug in development — it resolves on the deployed Vercel URL.

You should see: All 7 tetromino shapes spawn, rotate, collide, lock, and clear lines correctly.

3

Adjust game constants in the Code tab

Open the V0 Code tab and find the game speed constants — look for the initial drop interval (typically 800ms or similar) and the lines-per-level threshold. Adjust these if you want a slower start for beginners or a faster ramp for experts. The TetrominoEngine file contains the piece matrix arrays if you want to verify or modify rotation states.

Tip: These constants are usually near the top of the GameLoop file or in a separate constants.ts.

You should see: The game loop uses your new speed values after saving.

4

Publish to a live Vercel URL

Click the Share button at the top-right of the V0 editor, then open the Publish tab. Click 'Publish to Production'. Vercel builds and deploys in 30-60 seconds. The live URL is where you should test the real game experience — the setInterval double-speed bug only affects React StrictMode in development, and the deployed build runs correctly.

Tip: Test on the Vercel URL before sharing — this is the definitive version of the game.

You should see: A live .vercel.app URL is available; the game plays at the correct speed with no double-interval issue.

5

Pull via GitHub for advanced customization

In the V0 editor, open the Git panel and connect your GitHub account. Link the project to a new repository. V0 creates a branch named v0/main-{hash} that tracks your changes. Pull this branch locally to add sound effects via the Web Audio API, or to implement the Supabase leaderboard prompt without working in V0's browser editor.

Tip: Test on mobile after deploying — arrow keys won't work on touch screens; use the mobile touch controls prompt below.

You should see: The repository is available on GitHub; you can pull and run it locally with npm run dev.

The prompt pack

Copy-paste these straight into v0's chat to customize the Tetristemplate. Each one names this template's own components — no generic filler.

1

Adjust initial speed and scoring formula

Makes the game more beginner-friendly by slowing the initial drop and increases the scoring multiplier for multi-line clears.

Quick win
Paste into v0 chat
Change the initial drop interval in GameLoop to 800ms so the game starts slower for beginners. Update the level-up threshold in LineClearing from 10 lines to 15 lines per level. Update the Tetris scoring in LineClearing to: 1 line = 100 × level, 2 lines = 300 × level, 3 lines = 500 × level, 4 lines (Tetris) = 800 × level. Add a DIFFICULTY constant object at the top of the GameLoop file with these values so they are easy to change.
2

Add a Hold Piece feature

Adds the standard Hold Piece mechanic, letting players save one tetromino for a better moment.

Quick win
Paste into v0 chat
Add a Hold Piece feature to the GameStateReducer. Add a new state key heldPiece (null initially). When the player presses 'C', dispatch a HOLD action: if heldPiece is null, move currentPiece to heldPiece and spawn the nextPiece; if heldPiece exists, swap currentPiece and heldPiece. Add a holdUsedThisPiece boolean to GameStateReducer — reset on lock, prevent double-hold. Render the held piece in a NextPiecePreview-style box labeled 'HOLD' to the left of TetrisCanvas.
3

Add mobile on-screen touch controls

Makes the game playable on phones and tablets with a visible on-screen controller that mirrors keyboard inputs.

Medium
Paste into v0 chat
Add an on-screen controller below TetrisCanvas for mobile players. Render four directional buttons (← Move Left, → Move Right, ↓ Soft Drop, ⟳ Rotate) and a hard drop button labeled DROP using shadcn/ui Button components arranged with flexbox. Each button should dispatch the same action to GameStateReducer as the corresponding keyboard input. Add a fifth button for the Hold Piece ('C' action). Show the entire touch controller only when window.matchMedia('(pointer: coarse)').matches is true so it stays hidden on desktop.
4

Add localStorage high score persistence

Persists the player's best score in the browser so it survives page refreshes without requiring a database.

Medium
Paste into v0 chat
Add localStorage-based high score tracking to the game. After game over, compare the final score from GameStateReducer to the value stored under the key 'tetris-highscore' in localStorage. If the current score is higher, write the new value to localStorage and show 'New High Score!' in the game-over overlay. On initial mount of TetrisCanvas, read 'tetris-highscore' and display it as 'Best: X' in the top-right corner of the canvas using ctx.fillText.
5

Add Supabase global leaderboard

Adds a persistent global leaderboard backed by Supabase so players compete across devices and sessions.

Advanced
Paste into v0 chat
Add a global leaderboard backed by Supabase. After game over, show a shadcn/ui Dialog with a name input field and the player's final score and level from GameStateReducer. On submit, call a Server Action that inserts { player_name: string, score: number, level: number, lines_cleared: number } into a Supabase table named tetris_scores. Add RLS to the table: anonymous users can INSERT and SELECT but not UPDATE or DELETE. After inserting, re-fetch the top 10 rows ordered by score descending and render them as a leaderboard list inside the Dialog. Use NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY from the V0 Vars panel.

Gotchas when you extend it

The failures people actually hit when they push this template past its defaults — and the exact fix for each.

Tetris pieces fall at double speed in development — GameLoop setInterval fires twice

Why: React StrictMode in development double-invokes effects; without clearInterval cleanup in the useEffect, two setInterval calls stack up and pieces fall at twice the intended speed.

Fix: Store the interval id in a useRef and return a cleanup function from the useEffect that calls clearInterval.

Fix prompt — paste into v0
Tetris pieces fall at double speed in development because setInterval stacks up. Store the interval id in a ref: const intervalRef = useRef(). Set intervalRef.current = setInterval(...) and return () => clearInterval(intervalRef.current) from the useEffect.
Rotating tetrominoes at the left or right wall causes them to clip through or get stuck

Why: Rotation doesn't implement wall-kick logic from the Super Rotation System — the rotated piece matrix is placed as-is even if it overlaps a wall boundary.

Fix: After rotation, test for wall collision. If collision on X axis, try offsetting the piece by +1 then -1 on X before rejecting the rotation entirely.

Fix prompt — paste into v0
Rotating tetrominoes at the left/right walls causes them to clip through or get stuck. After rotating a piece, check for wall collision. If collision on X axis, try offsetting the piece by +1 then -1. If both fail, reject the rotation (keep original orientation).
Arrow keys scroll the browser page while playing instead of moving the tetromino

Why: Arrow key and Space events propagate to the window and trigger default scroll behavior unless explicitly cancelled.

Fix: Call e.preventDefault() in the keydown event handler for all game keys (ArrowLeft, ArrowRight, ArrowDown, ArrowUp, Space).

Fix prompt — paste into v0
Arrow keys scroll the page while playing Tetris. In the keyboard event handler, call e.preventDefault() for all arrow key and space key events before dispatching game actions.
The Tetris grid looks blurry on Retina / MacBook screens

Why: Canvas physical pixel dimensions don't scale with devicePixelRatio — a 300×600 CSS canvas is only 300×600 physical pixels on a 2x display instead of 600×1200.

Fix: Multiply canvas.width and canvas.height by window.devicePixelRatio, then call ctx.scale(devicePixelRatio, devicePixelRatio) once after getting the 2D context.

Fix prompt — paste into v0
The Tetris grid looks blurry on Retina/MacBook screens. In TetrisCanvas, multiply canvas.width and canvas.height by window.devicePixelRatio. Then call ctx.scale(devicePixelRatio, devicePixelRatio) once after getting the 2D context.

Template vs. custom — the honest call

A forked template gets you far, fast. Here's where it holds up, and where you'll outgrow it.

The template is enough when

  • You want a complete falling-blocks game to study the useReducer game state pattern and Canvas rendering loop
  • You're building a portfolio and need a recognizable, interactive demo that shows technical depth
  • You need a quick game for a hackathon, internal fun tool, or branded campaign
  • You want the TetrominoEngine and CollisionDetector as a starting point you can extend in your local editor

Go custom when

  • You need multiplayer battle Tetris with real-time synchronization (requires WebSockets or Supabase Realtime)
  • You want a polished commercial game with leveled difficulty curves, full sound design, and an achievement system
  • You need to ship to mobile app stores — requires React Native or native Swift/Kotlin, not a browser canvas game

RapidDev extends V0 game prototypes with leaderboards, analytics, and social sharing ready for launch.

Frequently asked questions

Is this template actually called 'Tetris' — is that a trademark issue?

Tetris is a trademarked name owned by The Tetris Company. This v0.dev community template is named Tetris in the community listing, and you can reference it by that name factually. However, if you ship a product based on this template, use a different name — 'falling blocks game', 'block puzzle', or your own brand name — to avoid trademark conflicts with commercial Tetris rights holders.

Is this template free to fork and use?

Yes. Forking v0.dev community templates is free with a v0.dev account. The fork itself consumes no credits. V0 chat prompts that modify the code do consume credits. Deploying to Vercel is free on the Hobby tier. Any third-party packages you add (shadcn/ui, canvas-confetti) are MIT licensed.

Can I use this template in a commercial product?

You own the code output from your V0 fork and can use it commercially. Remember to rename the game away from 'Tetris' in your product (see the trademark note above). The underlying libraries (Next.js, shadcn/ui) are MIT licensed and permit commercial use.

Why does my fork break in V0 preview — pieces fall at double speed?

This is the React StrictMode setInterval bug. In development, StrictMode double-invokes effects, causing two setInterval calls to stack without cleanup. The fix: store the interval ID in a useRef and return clearInterval from the useEffect. Crucially, this bug only appears in development — the deployed Vercel URL runs correctly.

The rotation doesn't work properly at the walls — how do I fix it?

The base template doesn't implement Super Rotation System wall kicks. Copy the fix_prompt from the 'Rotating tetrominoes at walls' gotcha above into V0 chat. It adds a +1/-1 X offset test after each rotation, which handles the most common edge cases without requiring a full SRS implementation.

How do I add sound effects to the game?

Pull the code locally via the GitHub Git panel and add sound effects using the Web Audio API. Create an AudioContext and use oscillators or AudioBuffer playback for line clears, tetromino locks, and game over. V0 chat can scaffold the AudioContext setup, but testing audio in V0's preview sandbox is unreliable — test on the deployed Vercel URL.

How do I connect a global leaderboard database?

Use the Supabase leaderboard prompt from the prompt pack above. Create a Supabase project, add a tetris_scores table with RLS for anonymous inserts and selects, then paste NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY into the V0 Vars panel. The prompt handles the Server Action and leaderboard UI.

Can RapidDev extend this template with a leaderboard and social sharing?

Yes. RapidDev extends V0 game prototypes with leaderboards, analytics, and social sharing ready for production launch. Visit rapidevelopers.com to discuss your game project.

Outgrowing the template?

RapidDev turns v0 prototypes into production apps — real auth, database, and payments — at $13K–$25K.

Book a free consultation

30-min call. No commitment.

Want this built for you?

We ship production apps at a fixed price — $13K–$25K, 6–10 weeks, source code yours. You've seen what it takes; we do it every week.

Get a fixed-price quote

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.