Tic Tac Toe Game Using Typescript
In this tutorial, We are going to create an interactive tic-tac-toe game in which two users can play against each other and the winner will be displayed to the winning user.
What Weâre Going to Create
Weâll build a Tic Tac Toe game focusing on event handling, DOM manipulation, and core game logic containing components.
- A game board.
- Various buttons to show the X or 0 which is the chance of the user.
- A reset button to reset the game board.
- A result component to show the winner.
Project Preview

Tic Tac Toe Game - HTML and CSS code
This HTML code sets up the structure for a Tic Tac Toe game, including a 3x3 grid of buttons for gameplay, a reset button, and the CSS add's up the styling to the buttons and the reset button on the game board.
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: lightcyan;
text-align: center;
}
.c {
height: 70vh;
display: flex;
justify-content: center;
align-items: center;
}
.g {
height: 60vmin;
width: 60vmin;
display: flex;
flex-wrap: wrap;
gap: 1.5vmin;
justify-content: center;
}
.b {
height: 18vmin;
width: 18vmin;
border-radius: 1rem;
border: none;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.3);
font-size: 8vmin;
color: red;
background-color: yellow;
}
#r {
padding: 1rem;
font-size: 1.25rem;
background: #191913;
color: white;
border-radius: 1rem;
border: none;
}
.b:hover {
background-color: chocolate;
}
#n {
padding: 1rem;
font-size: 1.25rem;
background: #191913;
color: white;
border-radius: 1rem;
border: none;
}
#m {
font-size: 8vmin;
}
.m-c {
height: 30vmin;
}
.h {
display: none;
}
</style>
</head>
<body>
<div class="m-c h">
<p id="m">Winner</p>
<button id="n">New Game</button>
</div>
<main>
<h1>Tic Tac Toe</h1>
<div class="c">
<div class="g">
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
</div>
</div>
<button id="r">Reset Game</button>
</main>
<script defer src="mains.js"></script>
</body>
</html>
In this example
- The layout is responsive, utilizing flexbox for centering and vmin units for scalable elements.
- Hover effects and shadow styling enhance user interaction and visual appeal.
- Game behavior is managed dynamically, with elements being shown or hidden based on game events.
Tic Tac Toe Game â TypeScript Logic
This TypeScript code implements the game logic for a Tic Tac Toe game, handling player turns, detecting wins or draws, and enabling game reset functionality.
const cells: NodeListOf<HTMLElement> = document.querySelectorAll('.b');
const resetButton: HTMLElement = document.querySelector('#r')!;
const newGameButton: HTMLElement = document.querySelector('#n')!;
const messageContainer: HTMLElement = document.querySelector('.m-c')!;
const message: HTMLElement = document.querySelector('#m')!;
let isPlayerO: boolean = true;
const winPatterns: number[][] = [
[0, 1, 2],
[0, 3, 6],
[0, 4, 8],
[1, 4, 7],
[2, 5, 8],
[2, 4, 6],
[3, 4, 5],
[6, 7, 8]
];
cells.forEach((cell) => {
cell.addEventListener('click', function () {
if (isPlayerO) {
cell.innerText = 'O';
cell.style.color = 'green';
isPlayerO = false;
(cell as HTMLButtonElement).disabled = true;
checkForWinner();
} else {
cell.innerText = 'X';
cell.style.color = 'black';
isPlayerO = true;
(cell as HTMLButtonElement).disabled = true;
checkForWinner();
}
});
});
const enableCells = (): void => {
cells.forEach((cell) => {
(cell as HTMLButtonElement).disabled = false;
cell.innerText = "";
});
};
const disableCells = (): void => {
cells.forEach((cell) => {
(cell as HTMLButtonElement).disabled = true;
});
};
const displayWinner = (winner: string): void => {
message.innerText = `Congratulations, Winner is ${winner}`;
messageContainer.classList.remove('h');
disableCells();
};
const checkForWinner = (): void => {
let winnerFound: boolean = false;
winPatterns.forEach((pattern) => {
let pos1Value: string = cells[pattern[0]].innerText;
let pos2Value: string = cells[pattern[1]].innerText;
let pos3Value: string = cells[pattern[2]].innerText;
if (pos1Value !== "" && pos2Value !== "" && pos3Value !== ""
&& pos1Value === pos2Value && pos2Value === pos3Value) {
displayWinner(pos1Value);
winnerFound = true;
return;
}
});
if (!winnerFound) {
const allCellsFilled: boolean = Array.prototype.slice.call(cells)
.every((cell) => cell.innerText !== "");
if (allCellsFilled) {
messageContainer.classList.remove('h');
message.innerText = 'Match Drawn';
}
}
};
const resetGame = (): void => {
isPlayerO = true;
enableCells();
messageContainer.classList.add('h');
};
newGameButton.addEventListener('click', resetGame);
resetButton.addEventListener('click', resetGame);
In this example
- HTML elements like game cells (.box) and buttons (#reset, #new-btn) are selected using querySelector, with explicit type definitions for type safety.
- The turnO flag alternates between true (Player O's turn) and false (Player X's turn), with the respective player's symbol displayed and the cell disabled after each move.
- The winPatterns array defines all possible winning combinations. The checkWinner function compares the current board state to these patterns and triggers showWinner if a match is found.
- The resetGame function clears the board, re-enables cells, and hides the message container. Both the #reset and #new-btn buttons trigger this reset.
Convert to JavaScript File
Now You need to convert the TypeScript file into JavaScript to render by browser. Use one of the following command.
npx tsc mains.ts
tsc mains.ts
- The command tsc mains.ts compiles the mains.js TypeScript file into a mains.js JavaScript file.
- It places the output in the same directory as the input file by default.
Complete Code
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: lightcyan;
text-align: center;
}
.c {
height: 70vh;
display: flex;
justify-content: center;
align-items: center;
}
.g {
height: 60vmin;
width: 60vmin;
display: flex;
flex-wrap: wrap;
gap: 1.5vmin;
justify-content: center;
}
.b {
height: 18vmin;
width: 18vmin;
border-radius: 1rem;
border: none;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.3);
font-size: 8vmin;
color: red;
background-color: yellow;
}
#r {
padding: 1rem;
font-size: 1.25rem;
background: #191913;
color: white;
border-radius: 1rem;
border: none;
}
.b:hover {
background-color: chocolate;
}
#n {
padding: 1rem;
font-size: 1.25rem;
background: #191913;
color: white;
border-radius: 1rem;
border: none;
}
#m {
font-size: 8vmin;
}
.m-c {
height: 30vmin;
}
.h {
display: none;
}
</style>
</head>
<body>
<div class="m-c h">
<p id="m">Winner</p>
<button id="n">New Game</button>
</div>
<main>
<h1>Tic Tac Toe</h1>
<div class="c">
<div class="g">
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
<button class="b"></button>
</div>
</div>
<button id="r">Reset Game</button>
</main>
<script defer>
const cells = document.querySelectorAll('.b');
const resetButton = document.querySelector('#r');
const newGameButton = document.querySelector('#n');
const messageContainer = document.querySelector('.m-c');
const message = document.querySelector('#m');
let isPlayerO = true;
const winPatterns = [
[0, 1, 2],
[0, 3, 6],
[0, 4, 8],
[1, 4, 7],
[2, 5, 8],
[2, 4, 6],
[3, 4, 5],
[6, 7, 8]
];
cells.forEach((cell) => {
cell.addEventListener('click', function () {
if (isPlayerO) {
cell.innerText = 'O';
cell.style.color = 'green';
isPlayerO = false;
cell.disabled = true;
checkForWinner();
} else {
cell.innerText = 'X';
cell.style.color = 'black';
isPlayerO = true;
cell.disabled = true;
checkForWinner();
}
});
});
const enableCells = () => {
cells.forEach((cell) => {
cell.disabled = false;
cell.innerText = "";
});
};
const disableCells = () => {
cells.forEach((cell) => {
cell.disabled = true;
});
};
const displayWinner = (winner) => {
message.innerText = `Congratulations, Winner is ${winner}`;
messageContainer.classList.remove('h');
disableCells();
};
const checkForWinner = () => {
let winnerFound = false;
winPatterns.forEach((pattern) => {
let pos1Value = cells[pattern[0]].innerText;
let pos2Value = cells[pattern[1]].innerText;
let pos3Value = cells[pattern[2]].innerText;
if (pos1Value !== "" && pos2Value !== "" && pos3Value !== ""
&& pos1Value === pos2Value && pos2Value === pos3Value) {
displayWinner(pos1Value);
winnerFound = true;
return;
}
});
if (!winnerFound) {
const allCellsFilled = Array.prototype.slice.call(cells).
every((cell) => cell.innerText !== "");
if (allCellsFilled) {
messageContainer.classList.remove('h');
message.innerText = 'Match Drawn';
}
}
};
const resetGame = () => {
isPlayerO = true;
enableCells();
messageContainer.classList.add('h');
};
newGameButton.addEventListener('click', resetGame);
resetButton.addEventListener('click', resetGame);
</script>
</body>
</html>