# Spin the Wheel > Build a Farcaster Frame **Published by:** [anyvoid.eth](https://paragraph.com/@anyvoid.eth/) **Published on:** 2025-02-24 **Categories:** farcaster, frame, vercel, spin-the-wheel **URL:** https://paragraph.com/@anyvoid.eth/spin-the-wheel ## Content In this article I will describe how I build "Spin the Wheel" frame. The idea of this frame came to me after seeing usage of a wheel in order to select winner for a raffle or for a NFT airdrop. So let's go an build a small adds and tracking free application!1. Initialyze the projectFirst create a new repository using gh gh repo createNext I've init a new react application with vite.jsnpm create vite@latest . --template reactI've selected TypeScript template At this point a default application is available when running npm run dev2. Install dependenciesFor this application I have used react-custom-roulette and react-confettinpm i react-custom-roulette react-confetti --legacy-peer-depsNote the usage of --legacy-peer-deps because both dependencies required react 18 and the project has been initialize with react 19. I could also downgrade react but I prefer force the usage of the libs in order to keep all dependencies to there latest release. Next I've just code a simple App.tsx module and pushed the code to the main repository branch.([]); const [newLabel, setNewLabel] = useState(""); const [selectedLabel, setSelectedLabel] = useState(null); const [spinning, setSpinning] = useState(false); const [showConfetti, setShowConfetti] = useState(false); const [prizeNumber, setPrizeNumber] = useState(0); const addLabel = (): void => { if (newLabel.trim() !== "") { const color = `#${Math.floor(Math.random() * 16777215).toString(16)}`; setLabels([...labels, { option: newLabel, style: { backgroundColor: color } }]); setNewLabel(""); } }; const removeLabel = (index: number): void => { setLabels(labels.filter((_, i: number): boolean => i !== index)); }; const spinWheel = (): void => { setSelectedLabel(null); setShowConfetti(false); if (labels.length === 0 || spinning) return; setSpinning(true); const selectedIndex = Math.floor(Math.random() * labels.length); setPrizeNumber(selectedIndex); }; const emojis: string[] = ["πŸŽ‰", "🎊", "πŸ†", "πŸ₯³", "πŸ‘", "πŸ”₯"]; return ( {showConfetti && } Spin the Wheel {selectedLabel && Congratulations {selectedLabel} {emojis[Math.floor(Math.random() * emojis.length)]}} Spin Wheel setNewLabel(e.target.value)} placeholder="Enter name" disabled={spinning} /> Add Participant {labels.length > 0 ? ( { setSpinning(false); setSelectedLabel(labels[prizeNumber].option); setShowConfetti(true); setTimeout(() => {setShowConfetti(false);}, 5000); }} backgroundColors={["#f9c74f", "#f94144", "#43aa8b", "#577590"]} textColors={["#fff"]} spinDuration={1.0} innerRadius={0} outerBorderWidth={2} radiusLineWidth={2} /> ) : ( No participants available )} {labels.map((label, index) => ( {label.option} removeLabel(index)}>Remove ))} © 2025 - anyvoid.eth - View on GitHub ); } export default App ">import { JSX, useState } from 'react'; import { Wheel } from "react-custom-roulette"; import Confetti from "react-confetti"; import './App.css'; function App(): JSX.Element { const [labels, setLabels] = useState<{option: string, style: { backgroundColor: string }}[]>([]); const [newLabel, setNewLabel] = useState(""); const [selectedLabel, setSelectedLabel] = useState(null); const [spinning, setSpinning] = useState(false); const [showConfetti, setShowConfetti] = useState(false); const [prizeNumber, setPrizeNumber] = useState(0); const addLabel = (): void => { if (newLabel.trim() !== "") { const color = `#${Math.floor(Math.random() * 16777215).toString(16)}`; setLabels([...labels, { option: newLabel, style: { backgroundColor: color } }]); setNewLabel(""); } }; const removeLabel = (index: number): void => { setLabels(labels.filter((_, i: number): boolean => i !== index)); }; const spinWheel = (): void => { setSelectedLabel(null); setShowConfetti(false); if (labels.length === 0 || spinning) return; setSpinning(true); const selectedIndex = Math.floor(Math.random() * labels.length); setPrizeNumber(selectedIndex); }; const emojis: string[] = ["πŸŽ‰", "🎊", "πŸ†", "πŸ₯³", "πŸ‘", "πŸ”₯"]; return (
{showConfetti && }

Spin the Wheel

{selectedLabel &&

Congratulations {selectedLabel} {emojis[Math.floor(Math.random() * emojis.length)]}

}
setNewLabel(e.target.value)} placeholder="Enter name" disabled={spinning} /> {labels.length > 0 ? (
{ setSpinning(false); setSelectedLabel(labels[prizeNumber].option); setShowConfetti(true); setTimeout(() => {setShowConfetti(false);}, 5000); }} backgroundColors={["#f9c74f", "#f94144", "#43aa8b", "#577590"]} textColors={["#fff"]} spinDuration={1.0} innerRadius={0} outerBorderWidth={2} radiusLineWidth={2} />
) : (

No participants available

)}
    {labels.map((label, index) => (
  • {label.option}
  • ))}
); } export default App 3. Deploy the ApplicationI have selected Vercel in order to host the application. The integration is so fast. After create an account, we can import git repository from "Add new > Project > Import Git Repository"Next I have to adjust build settingsπŸŽ‰ the application is now live. The build and deploy take few seconds. Vercel create free hostname based on the repository name. But Vercel also allow to link an external domain to the application in "Project Settings > Domains" πŸ™Œ So now the application is available at https://spin-wheel.anyvoid.xyz4. Configure FrameNow I can configure the frame on the domain configured in Vercel.npm install @farcaster/frame-sdk --legacy-peer-depsLoad the frame in main.tsxload frameAdd .well-known/farcaster.json.well-known/farcaster.jsonAdd fc:frame metadatafc:frame meta attributeNow as Vercel integration has been configured on the repository, I've just to push the updates on the main branch and the deploy will be automatically triggered. The application is now also accessible into a Farcaster frameRunning the application into Facaster FrameConclusionThe usage of vite, react and vercel allow to deploy an application very quickly. The usage of a custom domain on a project allow to personalized the project and keep consistency between applications.Thank you for taking the time to read this article, feel free to share your thoughts, experiences, or questions in the comments. ## Publication Information - [anyvoid.eth](https://paragraph.com/@anyvoid.eth/): Publication homepage - [All Posts](https://paragraph.com/@anyvoid.eth/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@anyvoid.eth): Subscribe to updates ## Optional - [Collect as NFT](https://paragraph.com/@anyvoid.eth/spin-the-wheel): Support the author by collecting this post - [View Collectors](https://paragraph.com/@anyvoid.eth/spin-the-wheel/collectors): See who has collected this post