Take Your Time
How a list of words inspired UNPARTY.
UNPARTY: Embrace Your Journey
The Power of Self-Reflection
One day I put on a pair of headphones and played "I Don't Want to Be" by Gavin DeGraw and wrote down every word I thought when I was listening to it. No filter, no stopping, just a list of everything I thought of myself and what I really wanted to be. Who I wanted to be. The next song that played was "Soundtrack 2 My Life" by Kid Cudi. I immediately stopped writing. Did not need that exsistential crisis.
When I started UNPARTY, it wasn't a clear path—there were many moments of introspection I had to face before I felt like I could build something that solved a problem. I had to take some accountability for where I was in life and why I felt the need to build this platform. I was in a moment where I felt like I had something to prove to everyone and myself. When you feel that way for too long , you begin to get stuck. It is an impossible rut to get out of because it requires you to admit defeat. You will never make everyone happy, so why try. I needed to get in the drivers seat and find some control and writing was the closest thing I could find that let me feel like I had complete control.
Writing has always been healing to me. It gives me a sense of control. Like I could write my own story. As a visual learner, it was always a bit of a challenge for me. Like I needed to see the words come to life to make sure the right emotion was being portrayed. I'm one of those people that when I close my eyes while I am listening to music I see in color. It is the most calming feeling I know and I tend to chase it. So when I write I feel like the words are often empty. How could I build something that helped me build out how I was feeling?
I often feel like everything good has been written before and it is up to the next generation to continue to build it. Add link to inspiration/stories. Something like "Check out the stories inspired UNPARTY" collection of books/media/etc
Touch Grass Challenge
TASK: SEE ME
Materials:
- Headphones
- Blank paper
- Pen or pencil
- Music player with one song
Steps:
-
Go outside.
-
Play your chosen song.
-
Write words describing yourself while listening. Don't edit.
-
Continue writing until the song ends.
-
Read your list.
-
Cross out words you disagree with.
-
On the back, write one goal based on the remaining words.
-
Read it back.
-
Throw it away.
-
Move Forward.
UNPARTY Challenge
Create a Word Search Web App
Core Functionality:
- Generate a word search based on user-input words
- No word key provided; users must find all words
- Allow repetition of words
User Flow:
-
Setup
- User enters song time length
- 5-second countdown for preparation
-
Word Input
- Input box appears with submit button
- User can submit words by pressing enter/return
- Input continues until time is up
-
Word Search
- Word search appears without a key
- Hints available to user
- Search continues until all words are found
-
Completion
- Word search disappears
- "LET IT GO" message displays
- After 3 seconds, options appear:
- Share
- Replay
- Close screen
Technical Requirements:
- Single-page web application
- Responsive design for various devices
- Smooth transitions between stages
- Efficient word search generation algorithm
- Hint system implementation
Bonus Features:
- Customizable grid size
- Option to save and load word lists
- Social media sharing integration
BUILD IT
Code Challenge
Step 1: Set up the project
- Open Visual Studio Code
- Open a terminal in VS Code (View > Terminal)
- Navigate to where you want to create your project
- Run the following commands:
Get Started
npx create-next-app@latest word-search-app
- When prompted, choose the following options:
Set Up
✔ Would you like to use TypeScript? … No
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
- Once the installation is complete, navigate into your new project directory:
Navigate
cd word-search-app
Step 2: Create basic components
- In the
src/appdirectory, create a new file calledpage.tsx. Replace its contents with:
import { useState } from 'react'
export default function Home() {
const [stage, setStage] = useState('setup')
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<h1 className="text-4xl font-bold mb-8">Word Search Game</h1>
{stage === 'setup' && <SetupStage setStage={setStage} />}
{stage === 'input' && <InputStage setStage={setStage} />}
{stage === 'search' && <SearchStage setStage={setStage} />}
{stage === 'complete' && <CompleteStage setStage={setStage} />}
</main>
)
}
function SetupStage({ setStage }) {
// Implementation coming soon
return <div>Setup Stage</div>
}
function InputStage({ setStage }) {
// Implementation coming soon
return <div>Input Stage</div>
}
function SearchStage({ setStage }) {
// Implementation coming soon
return <div>Search Stage</div>
}
function CompleteStage({ setStage }) {
// Implementation coming soon
return <div>Complete Stage</div>
}
You might encounter this TypeScript error when implementing the code:
- "useState" is not allowed in Server Components.ts(71001)
Don't worry! Here's how to fix this issue:
- Add
"use client";at the top of your file to indicate it's a Client Component.
Here's an example of how to update your code:
"use client";
These changes will resolve the TypeScript error and improve type safety in your code. You may see some additional errors, but don't worry we will get there!
Step 3: Run Your Server!
Run npm run dev to start the development server.
npm run dev
Your browser should open to a screen that looks similar to this:

Step 4: Implement the Setup Stage
Now that we have our basic component structure, let's implement the SetupStage function.
This stage will allow users to set the game duration before starting.
Replace the placeholder SetupStage function with the following code:
/**
* Renders the setup stage of the game.
* @param {Object} props - The component props.
* @param {Function} props.setStage - The function to set the stage of the game.
*/
function SetupStage({ setStage }: { setStage: (stage: string) => void }) {
const [time, setTime] = useState(60); // Default to 60 seconds
/**
* Handles the start button click event.
* Moves to the input stage if the time is greater than 0.
*/
const handleStart = () => {
if (time > 0) {
setStage('input'); // Move to the input stage
}
};
return (
<div className="text-center">
<h2 className="text-2xl font-bold mb-4">Set Up Your Game</h2>
<div className="mb-4">
<label htmlFor="time-input" className="block mb-2">
Song Time Length (seconds):
</label>
<input
id="time-input"
type="number"
value={time}
onChange={(e) => setTime(Math.max(1, parseInt(e.target.value) || 0))}
className="w-24 px-2 py-1 border rounded"
min="1"
/>
</div>
<button
onClick={handleStart}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Start Game
</button>
</div>
);
}
This function does the following:
- Uses the useState hook to manage the time state, defaulting to 60 seconds.
- Provides an input field for the user to set the song time length.
- Includes a start button that, when clicked, transitions to the 'input' stage using the setStage function.
- Uses Tailwind CSS classes for styling to maintain consistency with your project's design.
Step 5: Implement the InputStage
Now that we have our Setup Stage, let's create the Input Stage. This stage will allow users to input their one-word descriptions within the time limit they set.
Replace the placeholder InputStage function with the following code:
function InputStage({ setStage }: { setStage: (stage: string) => void }) {
const [words, setWords] = useState<string[]>([]);
const [currentWord, setCurrentWord] = useState('');
const [timeLeft, setTimeLeft] = useState(60); // You'll need to pass this from SetupStage
useEffect(() => {
if (timeLeft > 0) {
const timer = setTimeout(() => setTimeLeft(timeLeft - 1), 1000);
return () => clearTimeout(timer);
} else {
setStage('search');
}
}, [timeLeft, setStage]);
const handleAddWord = () => {
if (currentWord.trim() && !words.includes(currentWord.trim())) {
setWords([...words, currentWord.trim()]);
setCurrentWord('');
}
};
return (
<div className="text-center">
<h2 className="text-2xl font-bold mb-4">Enter Your Words</h2>
<div className="mb-4">
<p>Time left: {timeLeft} seconds</p>
</div>
<div className="mb-4">
<input
type="text"
value={currentWord}
onChange={(e) => setCurrentWord(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleAddWord()}
className="w-64 px-2 py-1 border rounded mr-2"
placeholder="Enter a word"
/>
<button
onClick={handleAddWord}
className="bg-green-500 hover:bg-green-700 text-white font-bold py-1 px-4 rounded"
>
Add Word
</button>
</div>
<div>
<h3 className="text-xl font-bold mb-2">Your Words:</h3>
<ul>
{words.map((word, index) => (
<li key={index}>{word}</li>
))}
</ul>
</div>
</div>
);
}
MAKE NOTE:
When implementing the InputStage, you may encounter a TypeScript error: ReferenceError: useEffect is not defined.
This error occurs because we're using the useEffect hook, which needs to be imported from React. To resolve this issue:
- At the top of your
page.tsxfile, update your React import statement to includeuseEffect:
import { useState, useEffect } from 'react';
- If you're using an IDE like Visual Studio Code, you might see red squiggly lines under
useEffectuntil you make this change. These lines indicate that TypeScript doesn't recognizeuseEffect. - After adding the import, the error should disappear, and TypeScript will correctly recognize the
useEffecthook. - Remember that whenever you use a new React hook (like
useState,useEffect,useContext, etc.), you need to import it from the 'react' package. - If you're curious about what
useEffectdoes, it's a hook that lets you perform side effects in function components. In our case, we're using it to create a timer that updates every second.
By keeping your imports up-to-date, you ensure that TypeScript can properly check your code and provide accurate error messages and autocompletion.
Step 6: Make your functions communicate
You'll need to modify your SetupStage to pass the time to InputStage. Update your Home component like this:
export default function Home() {
const [stage, setStage] = useState('setup');
const [gameTime, setGameTime] = useState(60);
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<h1 className="text-4xl font-bold mb-8">Word Search Game</h1>
{stage === 'setup' && <SetupStage setStage={setStage} setGameTime={setGameTime} />}
{stage === 'input' && <InputStage setStage={setStage} gameTime={gameTime} />}
{stage === 'search' && <SearchStage setStage={setStage} />}
{stage === 'complete' && <CompleteStage setStage={setStage} />}
</main>
)
}
And then update your SetupStage to use setGameTime:
function SetupStage({ setStage, setGameTime }: { setStage: (stage: string) => void, setGameTime: (time: number) => void }) {
const [time, setTime] = useState(60);
const handleStart = () => {
if (time > 0) {
setGameTime(time);
setStage('input');
}
};
// ... rest of the component remains the same
With these changes, your Input Stage is now functional. Users can input words within the time limit they set in the Setup Stage.
Step 7: Implement the Search Stage
Now that we have our countdown timer working and our words input is functioning, let's create our Search Stage function. This stage will generate a word search puzzle using the words input by the user and allow them to find these words.
Replace the placeholder SearchStage function with the following code:
function SearchStage({ setStage, words }: { setStage: (stage: string) => void, words: string[] }) {
const [grid, setGrid] = useState<string[][]>([]);
const [foundWords, setFoundWords] = useState<string[]>([]);
useEffect(() => {
// Generate the word search grid
const newGrid = generateWordSearch(words);
setGrid(newGrid);
}, [words]);
const handleWordFound = (word: string) => {
if (words.includes(word) && !foundWords.includes(word)) {
setFoundWords([...foundWords, word]);
if (foundWords.length + 1 === words.length) {
setStage('complete');
}
}
};
return (
<div className="text-center">
<h2 className="text-2xl font-bold mb-4">Find the Words!</h2>
<div className="mb-4 grid grid-cols-10 gap-1">
{grid.map((row, rowIndex) =>
row.map((letter, colIndex) => (
<div key={`${rowIndex}-${colIndex}`} className="w-8 h-8 border flex items-center justify-center">
{letter}
</div>
))
)}
</div>
<div className="mb-4">
<h3 className="text-xl font-bold mb-2">Words to Find:</h3>
<ul className="flex flex-wrap justify-center">
{words.map((word, index) => (
<li key={index} className={`mx-2 ${foundWords.includes(word) ? 'line-through text-gray-500' : ''}`}>
{word}
</li>
))}
</ul>
</div>
<button
onClick={() => setStage('complete')}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
End Game
</button>
</div>
);
}
// Helper function to generate word search
function generateWordSearch(words: string[]): string[][] {
// This is a simplified version. You might want to implement a more sophisticated algorithm.
const size = 10;
const grid: string[][] = Array(size).fill(null).map(() => Array(size).fill(''));
words.forEach(word => {
const row = Math.floor(Math.random() * size);
const col = Math.floor(Math.random() * (size - word.length + 1));
for (let i = 0; i < word.length; i++) {
grid[row][col + i] = word[i].toUpperCase();
}
});
// Fill in remaining spaces with random letters
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
if (grid[i][j] === '') {
grid[i][j] = String.fromCharCode(65 + Math.floor(Math.random() * 26));
}
}
}
return grid;
}
This implementation does the following:
- Uses
useStateto manage the word search grid and the list of found words. - Uses
useEffectto generate the word search grid when the component mounts. - Provides a function
handleWordFoundto mark words as found (Note: In this implementation, word finding is simulated with the "End Game" button. You'd need to implement actual word selection logic for a complete game). - Displays the word search grid and the list of words to find.
- Uses Tailwind CSS classes for styling to maintain consistency with your project's design.
You'll need to modify your Home component to pass the words to the SearchStage:
export default function Home() {
const [stage, setStage] = useState('setup');
const [gameTime, setGameTime] = useState(60);
const [words, setWords] = useState<string[]>([]);
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<h1 className="text-4xl font-bold mb-8">Word Search Game</h1>
{stage === 'setup' && <SetupStage setStage={setStage} setGameTime={setGameTime} />}
{stage === 'input' && <InputStage setStage={setStage} gameTime={gameTime} setWords={setWords} />}
{stage === 'search' && <SearchStage setStage={setStage} words={words} />}
{stage === 'complete' && <CompleteStage setStage={setStage} />}
</main>
)
}
To update the InputStage to use setWords, follow these steps:
- First, update the component's props to include
setWordsandgameTime:
function InputStage({ setStage, setWords, gameTime }: {
setStage: (stage: string) => void,
setWords: (words: string[]) => void,
gameTime: number
}) {
- Initialize the
timeLeftstate with gameTime:
const [timeLeft, setTimeLeft] = useState(gameTime);
- Modify the
useEffecthook to pass the words to the next stage when time runs out:
useEffect(() => {
if (timeLeft > 0) {
const timer = setTimeout(() => setTimeLeft(timeLeft - 1), 1000);
return () => clearTimeout(timer);
} else {
setWords(words); // Pass the words to the parent component
setStage('search');
}
}, [timeLeft, setStage, words, setWords]);
- Double check that your InputStage function reads like this:
function InputStage({ setStage, setWords, gameTime }: {
setStage: (stage: string) => void,
setWords: (words: string[]) => void,
gameTime: number
}) {
const [words, setLocalWords] = useState<string[]>([]);
const [currentWord, setCurrentWord] = useState('');
const [timeLeft, setTimeLeft] = useState(gameTime);
useEffect(() => {
if (timeLeft > 0) {
const timer = setTimeout(() => setTimeLeft(timeLeft - 1), 1000);
return () => clearTimeout(timer);
} else {
setWords(words); // Pass the words to the parent component
setStage('search');
}
}, [timeLeft, setStage, words, setWords]);
const handleAddWord = () => {
if (currentWord.trim() && !words.includes(currentWord.trim())) {
setLocalWords([...words, currentWord.trim()]);
setCurrentWord('');
}
};
Step 8: Implement the CompleteStage
Now that we have implemented the Search Stage, let's create the Complete Stage. This stage will be displayed when the player has found all the words or when the game ends. It will show a completion message and provide options to share the results, replay the game, or close the screen.
Replace the placeholder CompleteStage function with the following code:
function CompleteStage({ setStage, words, foundWords }: {
setStage: (stage: string) => void,
words: string[],
foundWords: string[]
}) {
const [showOptions, setShowOptions] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setShowOptions(true);
}, 3000);
return () => clearTimeout(timer);
}, []);
const handleShare = () => {
// Implement share functionality
// This could open a modal with share options or copy results to clipboard
console.log('Share functionality to be implemented');
};
const handleReplay = () => {
setStage('setup');
};
const handleClose = () => {
// Implement close functionality
// This could redirect to a home page or close the game window
console.log('Close functionality to be implemented');
};
return (
<div className="text-center">
<h2 className="text-4xl font-bold mb-8">LET IT GO!</h2>
<p className="text-xl mb-4">
You found {foundWords.length} out of {words.length} words.
</p>
{showOptions ? (
<div className="space-x-4">
<button
onClick={handleShare}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Share
</button>
<button
onClick={handleReplay}
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
>
Replay
</button>
<button
onClick={handleClose}
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
>
Close
</button>
</div>
) : (
<p>Options will appear in a moment...</p>
)}
</div>
);
}
This implementation does the following:
- Displays a "LET IT GO!" message as requested in the original challenge.
- Shows the number of words found by the player.
- Uses useState and useEffect to implement a 3-second delay before showing the options.
- Provides three buttons for sharing, replaying, and closing the game.
- Uses Tailwind CSS classes for styling to maintain consistency with your project's design.
You'll need to modify your Home component to pass the necessary props to the CompleteStage:
export default function Home() {
const [stage, setStage] = useState('setup');
const [gameTime, setGameTime] = useState(60);
const [words, setWords] = useState<string[]>([]);
const [foundWords, setFoundWords] = useState<string[]>([]);
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<h1 className="text-4xl font-bold mb-8">Word Search Game</h1>
{stage === 'setup' && <SetupStage setStage={setStage} setGameTime={setGameTime} />}
{stage === 'input' && <InputStage setStage={setStage} setWords={setWords} gameTime={gameTime} />}
{stage === 'search' && <SearchStage setStage={setStage} words={words} setFoundWords={setFoundWords} />}
{stage === 'complete' && <CompleteStage setStage={setStage} words={words} foundWords={foundWords} />}
</main>
)
}
Also, update your SearchStage to use setFoundWords:
function SearchStage({ setStage, words, setFoundWords }: {
setStage: (stage: string) => void,
words: string[],
setFoundWords: (words: string[]) => void
}) {
const [foundWordsLocal, setFoundWordsLocal] = useState<string[]>([]);
// ... existing code ...
const handleWordFound = (word: string) => {
if (words.includes(word) && !foundWordsLocal.includes(word)) {
const newFoundWords = [...foundWordsLocal, word];
setFoundWordsLocal(newFoundWords);
setFoundWords(newFoundWords);
if (newFoundWords.length === words.length) {
setStage('complete');
}
}
};
// ... rest of the component remains the same
Double Check: SearchStage Function
function SearchStage({ setStage, words, setFoundWords }: {
setStage: (stage: string) => void,
words: string[],
setFoundWords: (words: string[]) => void
}) {
const [grid, setGrid] = useState<string[][]>([]);
const [foundWordsLocal, setFoundWordsLocal] = useState<string[]>([]);
useEffect(() => {
// Generate the word search grid
const newGrid = generateWordSearch(words);
setGrid(newGrid);
}, [words]);
const handleWordFound = (word: string) => {
if (words.includes(word) && !foundWordsLocal.includes(word)) {
const newFoundWords = [...foundWordsLocal, word];
setFoundWordsLocal(newFoundWords);
setFoundWords(newFoundWords);
if (newFoundWords.length === words.length) {
setStage('complete');
}
}
};
return (
<div className="text-center">
<h2 className="text-2xl font-bold mb-4">Find the Words!</h2>
<div className="mb-4 grid grid-cols-10 gap-1">
{grid.map((row, rowIndex) =>
row.map((letter, colIndex) => (
<div key={`${rowIndex}-${colIndex}`} className="w-8 h-8 border flex items-center justify-center">
{letter}
</div>
))
)}
</div>
<div className="mb-4">
<h3 className="text-xl font-bold mb-2">Words to Find:</h3>
<ul className="flex flex-wrap justify-center">
{words.map((word, index) => (
<li
key={index}
className={`mx-2 ${foundWordsLocal.includes(word) ? 'line-through text-gray-500' : ''}`}
onClick={() => handleWordFound(word)} // Added for demonstration
>
{word}
</li>
))}
</ul>
</div>
<button
onClick={() => setStage('complete')}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
End Game
</button>
</div>
);
}
That's It! You've Created a Word Search Game
By following these steps, you now have a basic starting point for creating a word search game using Next.js and React. Let's recap what we've accomplished:
- Set up a Next.js project
- Created a Setup Stage for game configuration
- Implemented an Input Stage for word collection
- Developed a Search Stage with a word search grid
- Added a Complete Stage to wrap up the game
While this implementation provides a solid foundation, there are many ways you can expand and improve your game:
- Implement actual word selection logic in the grid
- Add more sophisticated word placement algorithms (diagonal, backwards, etc.)
- Improve the UI/UX with animations and better styling
- Implement actual sharing functionality
- Add difficulty levels or themes
Remember, building a game is an iterative process. Start with this basic version and gradually add features and improvements as you go.
You can follow updates to this tutorial and find more coding challenges at:
Happy coding, and have fun expanding your word search game!
NOTE: Don't forget to test your game thoroughly and gather feedback from users. Their input can be invaluable in making your game more enjoyable and identifying areas for improvement.
Create a Word Search Web App with NextJS
Welcome to this exciting challenge where you'll build a dynamic Word Search Web App using Next.js! This project will test your React skills and introduce you to game development concepts in a web environment.
In this tutorial, we'll create a word search game where users can:
- Set a time limit for word input
- Enter their own words to be hidden in the puzzle
- Search for words in a generated grid
We'll break down the process into manageable steps, focusing on:
- Setting up a Next.js project and creating the game's main components
- Implementing game logic for each stage (Setup, Input, Search, and Complete)
- Managing state and user interactions throughout the game flow
By the end of this tutorial, you'll have a functional word search game and a solid foundation for further enhancements.
Next steps to challenge yourself:
- Implement diagonal and backward word placements
- Add difficulty levels (e.g., larger grids, shorter time limits)
- Create a scoring system based on word length and find time
START SOMEWHERE:

Quick Challenge
Building takes time, and sometimes you just need a confidence boost. It's normal to feel like progress isn't fast enough, but remember: steady growth is sustainable growth. Try this json challenge to practice your skills.
The "Delete Your Doubts" Challenge
This challenge is designed to help you practice coding while metaphorically deleting your doubts and negative thoughts.
- Create a file named
doubts.jsonwith some sample "doubt" data. - Write a script to "delete" these doubts using the provided code snippet as inspiration.
- For each doubt deleted, add a positive affirmation or lesson learned.
Sample doubts.json
{
"doubts": [
{
"id": "doubt1",
"content": "I'm not learning fast enough"
},
{
"id": "doubt2",
"content": "My project isn't growing quickly"
},
{
"id": "doubt3",
"content": "I don't know enough to succeed"
}
]
}
Challenge Steps
- Read the
doubts.jsonfile. - For each doubt, use the DELETE request to "remove" it.
- After each deletion, log a positive message or lesson learned.
- Keep track of your progress and celebrate small wins!
Remember, the goal is to have fun while learning. Take your time, experiment, and don't be afraid to make mistakes – they're part of the growth process!
Delete Your Doubts
curl -X DELETE https://api.unparty.dev/v1/doubts/{doubt_id} \
-H "Authorization: Bearer {your_growth_token}"
Bonus Challenge
Try to implement error handling in your script. What if a doubt doesn't exist? How would you handle that gracefully?
Positive Response
{
"message": "Doubt successfully deleted",
"affirmation": "You're making progress, even when you can't see it!",
"doubts_remaining": 2
}
Remember: Just as in coding, growth is iterative. Celebrate each doubt you overcome, and keep going!