How to Build a Multiplayer Tic-Tac-Toe Game with Solana Wallet Integration

dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 07:36:43
Introduction

In this comprehensive tutorial, we'll create a web-based Tic-Tac-Toe game with real-time multiplayer functionality and Solana wallet integration. This project will allow players to connect their Solana wallets, find opponents, and play games with the option to record results on the blockchain.

Features
  •     Real-time multiplayer Tic-Tac-Toe
  •     Solana wallet authentication
  •     Game lobby system
  •     On-chain game history (optional)
  •     Winner payouts (optional)
Tech Stack
  •     Frontend: React + TailwindCSS
  •     Blockchain: Solana Web3.js + Devnet
  •     Wallet: Phantom + Solana Wallet Adapter
  •     Realtime Communication: Firebase Realtime Database
  •     Backend: Firebase Functions (optional Express alternative)
Setup Instructions
Prerequisites
  •     Node.js (v16 or later)
  •     Yarn or npm
  •     Phantom Wallet (or other Solana wallet)
  •     Firebase account (free tier sufficient)
  •     Basic understanding of JavaScript and React
dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 07:38:12
Step 1: Initialize the Project
npx create-react-app solana-tic-tac-toe --template typescript
cd solana-tic-tac-toe
yarn add @solana/web3.js @solana/wallet-adapter-react @solana/wallet-adapter-react-ui @solana/wallet-adapter-wallets @solana/wallet-adapter-base firebase @chakra-ui/react @emotion/react @emotion/styled framer-motion
yarn add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 10:20:26
Step 2: Configure TailwindCSS

Update  tailwind.config.js:

module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", ], theme: { extend: {}, }, plugins: [], }

Add to src/index.css:

@tailwind base; @tailwind components; @tailwind utilities;
dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 10:33:55
Step 3. Firebase Setup

Create a new Firebase project and enable:

Realtime Database Authentication (anonymous or email/password)

Add your Firebase config to src/firebase.ts: import { initializeApp } from 'firebase/app' import { getDatabase } from 'firebase/database' const firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", databaseURL: "YOUR_DATABASE_URL", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_SENDER_ID", appId: "YOUR_APP_ID" }; const app = initializeApp(firebaseConfig) const database = getDatabase(app) export { database }

dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 11:07:32
Step 4. Wallet Integration

Create src/components/WalletContextProvider.tsx: import React, { useMemo } from 'react' import { WalletAdapterNetwork } from '@solana/wallet-adapter-base' import { WalletModalProvider } from '@solana/wallet-adapter-react-ui' import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react' import { PhantomWalletAdapter } from '@solana/wallet-adapter-wallets' import { clusterApiUrl } from '@solana/web3.js' require('@solana/wallet-adapter-react-ui/styles.css') export const WalletContextProvider = ({ children }: { children: React.ReactNode }) => { const network = WalletAdapterNetwork.Devnet const endpoint = useMemo(() => clusterApiUrl(network), [network]) const wallets = useMemo(() => [new PhantomWalletAdapter()], []) return ( {children} ) }

dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 11:28:50
Step 5. Game Components

Create src/components/GameBoard.tsx:

import React from 'react' type BoardProps = { board: string[] onCellClick: (index: number) => void disabled: boolean }

export const GameBoard = ({ board, onCellClick, disabled }: BoardProps) => {
  return (
    <div className="grid grid-cols-3 gap-2 w-64 h-64 mx-auto">
      {board.map((cell, index) => (
        <button
          key={index}
          onClick={() => onCellClick(index)}
          disabled={disabled || !!cell}
          className="flex items-center justify-center text-4xl font-bold bg-gray-200 hover:bg-gray-300 rounded-lg h-20 w-20"
        >
          {cell}
        </button>
      ))}
    </div>
  )
}


dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 11:35:33
Step 6: Multiplayer Logic

Create src/hooks/useGame.ts for game state management: import { useState, useEffect } from 'react' import { ref, onValue, set, push, update } from 'firebase/database' import { database } from '../firebase' import { useWallet } from '@solana/wallet-adapter-react' type GameState = { board: string[] currentPlayer: 'X' | 'O' winner: string | null playerX: string | null playerO: string | null } export const useGame = (gameId?: string) => { const { publicKey } = useWallet() const [gameState, setGameState] = useState({ board: Array(9).fill(''), currentPlayer: 'X', winner: null, playerX: null, playerO: null }) const [isLoading, setIsLoading] = useState(true) const [isPlayer, setIsPlayer] = useState(false) const [playerSymbol, setPlayerSymbol] = useState<'X' | 'O' | null>(null) // Game logic implementation here // Including Firebase realtime updates, move validation, etc. return { gameState, isLoading, isPlayer, playerSymbol, makeMove: (cellIndex: number) => { // Implement move logic }, createGame: async () => { // Implement game creation }, joinGame: async (gameId: string) => { // Implement game joining } } }

dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 11:37:05
Step 7: Main App Component

Update src/App.tsx: import React from 'react' import { WalletContextProvider } from './components/WalletContextProvider' import { WalletMultiButton } from '@solana/wallet-adapter-react-ui' import { useGame } from './hooks/useGame' import { GameBoard } from './components/GameBoard' function App() { const { gameState, isLoading, isPlayer, playerSymbol, makeMove, createGame, joinGame } = useGame() if (isLoading) return

Loading...
return (

Solana Tic-Tac-Toe

{!gameState.playerX || !gameState.playerO ? (
) : (
{gameState.winner ? (

{gameState.winner === 'draw' ? "It's a draw!" : `${gameState.winner} wins!`}

) : (

Current turn: {gameState.currentPlayer} {isPlayer && ` (You're ${playerSymbol})`}

)}
)}
) } export default function WrappedApp() { return ( ) }

dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 11:37:46
Step 8: On-Chain Integration (Optional)

Create src/utils/solana.ts for blockchain interactions: import { Connection, PublicKey, Transaction, SystemProgram } from '@solana/web3.js' const connection = new Connection('https://api.devnet.solana.com') export const recordGameResult = async ( winner: PublicKey, loser: PublicKey, amount: number ) => { const transaction = new Transaction().add( SystemProgram.transfer({ fromPubkey: loser, toPubkey: winner, lamports: amount }) ) // In a real app, you'd sign and send this transaction properly return transaction } export const getGameHistory = async (player: PublicKey) => { // Implement fetching game history from your on-chain program return [] }

dhtml
Admin
Inscrit depuis le: 2025-08-24 08:08:05
2025-06-01 11:39:06
Deployment
1. Build the React App
bash
yarn build
2. Deploy to Firebase Hosting
bash
yarn add -D firebase-tools
firebase login
firebase init
# Select Hosting and follow prompts
firebase deploy
Facebook X (Twitter) Instagram LinkedIn Telegram WhatsApp