Wordle in React: Picking Up Where The Others Left Off!
Tags: tutorial, programming, react, beginners
Published: 2024-01-27 17:09:51

### Learn how to create the guess distribution, the landing page, and all the cool features that are in the original Wordle game!

![main image](https://cdn-images-1.medium.com/max/3200/1*04y78fYjEgaDFNJifiPfUA.png)

I’ve recently decided I would like to improve my React skills. Sure, I’ve followed [the official React tic-tac-toe tutorial](https://react.dev/learn/tutorial-tic-tac-toe) before, but things still didn’t click for me. I knew the solution was to create a fun project, so I found a [Wordle](https://www.nytimes.com/games/wordle/index.html) tutorial! It’s called [**Make a Wordle Clone with React** by Net Ninja](https://www.youtube.com/watch?v=ZSWl5UwhHcs&list=PL4cUxeGkcC9gXdVXVJBmHpSI7zCEcjLUX), and that was a great start.

Unfortunately, this project wasn’t close *enough* to the real game. Don’t get me wrong, it had the main functionality, which is very cool to implement, but I was passionate about adding the missing features that appear in the original Wordle!

In this tutorial, we will grab the Wordle-clone project that many people on YouTube created, and take it to the finish line! In a future blog post, I will show you how to theme your game with more exotic topics than ‘5 letter words’ :D

|![*So many React Wordle tutorials and they all stop at the same stage*](https://cdn-images-1.medium.com/max/3332/1*smSIeuSh9qv15UAB5h2lkQ.png)|
|---|
|*So many React Wordle tutorials and they all stop at the same stage*|

To make things clear, I will refer to the code I produced by following *Net Ninja*’s tutorial as *“the base code”*. [Here](https://github.com/iamshaunjp/React-Wordle/tree/lesson-16) is a link, in case you want to check it out.



The agenda for this blog post is:

 1. Useful React tips for beginners

 2. Quick overview of the base code we are starting with

 3. The virtual keyboard

 4. The post-game modal

 5. The landing page

 6. Calculating game index



Shall we begin?

![Yay!](https://cdn-images-1.medium.com/max/2240/1*kKUQ6C16pNQyaDCTAqa97A.png)

---

## Useful React tips for beginners

I know it all looks overwhelming when you are just starting with React, so let me make things a bit clearer!

1. There are two Reacts out there: *React Native* and *React/ReactJs*. ReactJS is for making front-end websites, and this is the one we are using today.

1. Creating a new project: Super simple, follow [this](https://legacy.reactjs.org/docs/create-a-new-react-app.html#create-react-app) paragraph and you are good to go. If everything goes well, your browser will open a new tab for `localhost:3000` with the default starting page:

|![*The starting page*](https://cdn-images-1.medium.com/max/2478/1*F7UzAdZNFSC823X5Fz33sQ.png)|
|---|
|*The starting page*|

1. Running the app locally: You can either use `npm start` in the command line or hover above `start` in `package.json` and then click `Run Script`.

|![Running the app locally](https://cdn-images-1.medium.com/max/3200/1*szYUIjmZKZgYkUHh9l7F2w.png)|
|---|
|*Running the app locally*|

* You don’t need to restart when you want to see your code changes, it will update automatically.

* You should try refreshing the browser page when you are fixing an error — because those sometimes stay on the screen even when the issue is fixed.

1. There is a useful VS code extension called ***ES7+ React/Redux/React-Native snippets***. With that installed, you can create a new component file, type in `rfc` and it will auto-complete the structure of the component.

Here are some solutions for problems you will probably encounter:

1. Double rendering: When you start printing to the console you will quickly notice that everything is rendering twice. This is not a bug in your code! This is a React thing. This does not happen in Production, just locally in the development environment. You can read more about it [here](https://stackoverflow.com/questions/48846289/why-is-my-react-component-is-rendering-twice).

1. Syntax tips and tricks:

* The error *“JSX expressions must have one parent element”* means you need to wrap your HTML elements with empty tags: `<><your code></>`.

* The syntax for mapping over a JS array to produce HTML elements is as follows:

{% gist https://gist.github.com/cupofcodeblog/c9c1e9c8f7906635e1683c9660c968f8.js %}

* The syntax for styling inside the HTML: `<h1 style={{paddingLeft: “70px”}}>` . Notice the double curly brackets, the camelCase, and the stringifying of the value only (but not the key).

1. Updates via the **UseState** hook do not reflect immediately, which means setting a value and then immediately getting it, won’t work as you intended. You can read more about it [here](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately).

```
setMovies(result);
console.log(movies) // movies!==result
```

## Deploying to an App Deployment Platform

Excitement to share your final product is an appropriate outcome of following this tutorial, and Netlify is the way to do so. [Netlify](https://www.netlify.com/) is a website that hosts your project for free!

1. Uploading to [Netlify](https://www.netlify.com/):

You can watch the full tutorial [here](https://youtu.be/ZW1Q8rO18ZI?si=Y0J15gJAI5k00Dph&t=173), but the TLDR is:
`build` the project (you can find it in the package.json file, just below `start`). This will create a build directory with all the needed files. Then, sign up to Netlify, and upload the `build` directory. It’s that simple!

I recommend you give it a try now and deploy the project you have as it is currently, before adding code. This way, you’d know if the blank page you see is a code issue or a deployment issue due to it being your first time using the platform.

2. Speaking of a blank page, let me introduce you to the *white screen of death*:

This refers to a situation where our React application appears to have crashed and only displays a blank white screen. This is very frustrating, especially when the app works fine on the laptop but not on the phone (and how do you debug there?!).

This is usually caused by JS code such as Date formatting, regex expressions, etc. I quickly learned that Chrome and Android phones are more forgiving than iPhones and some browsers on Mac (Safari, Firefox) so I highly recommend checking your website there periodically. If you see an error when opening the website on an iPhone, you will probably encounter the issue in Firefox on the Mac as well, where you have developer tools and can see the error.

3. The definition of done of any feature should include testing the iPhone view. Checking it via the developer tools’ responsive view isn’t enough, because it doesn’t give an accurate display. You’ll see examples of that later in the blog post.

Now you are ready to start!

![meme](https://cdn-images-1.medium.com/max/2000/0*El0E8bN6OuHVqM6L.jpg)

---

## The Base Code

No need to show here how to do those steps because [the YouTube tutorial](https://www.youtube.com/watch?v=ZSWl5UwhHcs&list=PL4cUxeGkcC9gXdVXVJBmHpSI7zCEcjLUX) does it very well. I’ll detail the state of the project post the tutorial, to connect you to the rest of the blog post.

The base code from *Net Ninja*’s GitHub can be found [here](https://github.com/iamshaunjp/React-Wordle/tree/lesson-16). You can either fork his code and come join me now, or revisit this blog post after following his tutorial!

### If you want to pull the base code:

 1. **Pull the code from Net Ninja**:
a. Open a terminal in your projects directory.
b. Run the following commands one by one:

```
git clone https://github.com/iamshaunjp/React-Wordle.git
cd .\React-Wordle\
git pull origin lesson-16 --allow-unrelated-histories
```

* You might need to resolve conflicts in `README.md`

c. Edit the `README.md` to be yours, and don't forget to credit *Net Ninja* for the base code!

d. Now you have the code locally!

2. **Make sure it runs smoothly**:
 a. Open your React-Wordle project in your IDE and open a new terminal
 b. Install dependencies by running the following commands one by one:

```
npm i
npm i json-server
```

c. Run the json-server:
 i. Open a new terminal
ii. Run the command `json-server .\data\db.json --port 3001`

d. Open `package.json`, hover above "build" and click "Run Script"

e. A new tab will open and you’ll see the game!

3. **Connect the project to your GitHub repository**:
 a. Create a new repo at GitHub.
 b. Open the terminal in your project directory.
 c. Run the following commands one by one:  
```
git remote set-url origin <URL_TO_GITHUB_REPO>
git add .
git commit -m "Starting with NetNinja's base code"
git push origin main
```

d. Now your code is in your GitHub repository!

|![Run the base code locally](https://cdn-images-1.medium.com/max/2838/1*-NZ3Wygh8IudrEiCp40VRw.png)|
|---|
|*Run the base code locally*|

### Overview of the base code

When you run the code you will see the page with the grid and the keyboard. The keyboard is not clickable and is here to show you all the letters and their colors. Above the grid there is the solution for this round, and the current guess the user is typing (for testing purposes).

Every time the user types a guess and presses enter, the tiles flip and color with green, yellow, or grey. The game ends when the user guesses the solution word or when the user does not manage to guess the solution word within 6 tries. Then, a modal pops up with a winning/losing message, the solution word, and the number of guesses it took.

**Please note** that every time you refresh the page, there will be a different solution word. The original Wordle game gives one word per day, and we will be coding this feature in today’s blog post! :D

![*Screenshots from [Net Ninja’s YouTube tutorial]*(https://www.youtube.com/watch?v=ZSWl5UwhHcs&list=PL4cUxeGkcC9gXdVXVJBmHpSI7zCEcjLUX)](https://cdn-images-1.medium.com/max/3272/1*x-dmwq1Q_hnME4dKAvn5EA.png)|
|---|
|*Screenshots from [Net Ninja’s YouTube tutorial](https://www.youtube.com/watch?v=ZSWl5UwhHcs&list=PL4cUxeGkcC9gXdVXVJBmHpSI7zCEcjLUX)*|

My favorite part was the flipping tiles — I’ve never made CSS animations before — and now I know how! :D

### This won’t work on Netlify!

If you try to deploy the base code as it is now in [Netlify](https://www.netlify.com/) **it won’t run**! This is because the base code is pulling the solution words from the JSON file by running `json-server` in a separate terminal, which simulates a RESTful API. Then, the line `fetch(‘http://localhost:3001/solutions')` breaks in Netlify.

Using the `json-server` package doesn’t work in Netlify because Netlify can only host static pages (with no servers running on parallel terminals).

**The solution**: Uninstall this package (`npm uninstall json-server`) and modify the code to simply call the local file: `import solutions from './data/db.json';`

---

## The Virtual Keyboard

The keyboard in the original game has two roles: Visual and functional.

First, show the color of the letters:

* Letters you haven’t tried yet — light grey

* Letters you guessed correctly in the right place — green

* Letters you guessed that are in the solution word but not in the right place — yellow

* Letters you have tried but do not appear in the word at all — dark grey

The second purpose of the keyboard is to populate the grid tiles. The user can click the virtual keyboard and it will function the same way as a regular keyboard, which is useful for users on mobile and Padlets.

The keyboard we start with from the base code does not have any functionality, and it doesn’t look like a regular keyboard, so we are now going to fix that!

![keyboard before and after](https://cdn-images-1.medium.com/max/2038/1*0SrzRvTaBk5Gd-0hvX0Kvg.png)

### Making It Look Like A Real Keyboard

First thing first — the keyboard is in lowercase letters! The original Wordle’s keyboard (and any keyboard in the world) has uppercase letters, so let’s change that!

In the base code, the keyboard is created using a JSON object called letters:

```
{
  "letters": [
    {"key": "a"},
    {"key": "b"},
  ...
}
```

Then the Keypad component fetches that object:
{% gist https://gist.github.com/cupofcodeblog/81d816af7392fe3c0962f80a41890003.js %}
We can simplify this! Remove it altogether and create an array of letters inside the component. No need for fetching and no need for useEffect hook! Tip: Don’t forget to order them in the [qwerty keyboard layout](https://en.wikipedia.org/wiki/QWERTY).

Note that even though it’s tempting, you shouldn’t change the letters to uppercase. The user is typing in lowercase (like the letters you are reading now), and the function will compare them to what would now be uppercase letters, and will always return a mismatch. So how do we get the uppercase visuals? With the CSS file!

```
.keypad > div {
  ...
  text-transform: uppercase;
}
```

If you run your app now, you might see the keyboard’s keys spilling to the wrong places: P goes to the second row, and L goes to the third row, depending on the width of the window. I fixed that by dividing my array of letters into three.

Now, to build the keyboard, I’m looping over the 3 arrays. This solution makes it very easy to add “Enter” and “Backspace” buttons: I just add them separately before and after the third loop!
{% gist https://gist.github.com/cupofcodeblog/ddec96861e5b195ba84ed60ed3775e19.js %}
### Making the Virtual Keyboard Clickable

The most straightforward approach here is to add “handleKeyup” function as the keys’ “onClick” event. Unfortunately, it didn’t work.

I couldn’t get the virtual keyboard to give the “handleKeyup” function the right parameters to make it work (something about it being an event handler and this `{key}` parameter). This caused me to use a workaround:

I took the content of handleKeyup and wrapped it in a new function, one that doesn’t have a weird `{key}` parameter. That way I had one source of truth to handling the user typings!

```
const useWordle = (solution) => {
  ...
  const handleKeyup = ({ key }) => {
    handleKeyupVirtual(key)
    return
  }
  ...
  return {
    ...
    handleKeyupVirtual
  };
```

To use this new function, we need to output it from the useWordle hook and pass it to the Keypad component:

```
export default function Wordle({...}) {
  const { ..., handleKeyupVirtual} = useWordle(...);
  ...
  <Keypad usedKeys={usedKeys} handleKeyupVirtual={handleKeyupVirtual} />
  ...
}
```

And then in Keypad.js:

```
return (
  ...
  <div key="Enter" style={{width: "15%", fontSize:"13px"}}
     onClick={() => handleKeyupVirtual("Enter")}>Enter</div>
...
```

### Fixing a Tiny Bug

When I opened the game on the iPhone I discovered a problem: The backspace character is very small, regardless of the font size.

![keyboard bug](https://cdn-images-1.medium.com/max/2000/1*sfvPSQl31GsnrASozEaMuQ.jpeg)

How do we solve this? I checked how the original game did it: I opened the NY Times Wordle with the developer tools and saw they used an image instead of a character, so I did the same.

After finding an appropriate image online and placing it on my keyboard, the image was stuck at the top of the key and I wasn’t able to resolve it with direct CSS modification: When I tried to modify the margin or the padding, it affected the entire row. It was not as easy as it initially looked!

|![*When I added a margin to the backspace image — it affected the entire keyboard row*](https://cdn-images-1.medium.com/max/2000/1*dZDu_hLTHQNWh5nJ0RGhnA.png)|
|---|
|*When I added a margin to the backspace image — it affected the entire keyboard row*|

Eventually, I did find a trick on [Stack overflow](https://stackoverflow.com/questions/7273338/how-to-vertically-align-an-image-inside-a-div): Add the “no wrap” white-space property to the backspace div, and add inside it a helper span. Now it’s in a perfect size!
{% gist https://gist.github.com/cupofcodeblog/dbd03141585174adf47bcacc03dbfb0b.js %}


### Final Touches

The keyboard is functioning and it looks good, but there are a few more things we can do to make it perfect!

**Adding delay to the key color change**

You might have noticed that when you play the original game, the keyboard key color updates only after the grid row is colored. In our game at the moment, the keyboard updates quicker because of the tiles’ animation.

I was looking into delaying components in react when I suddenly realized there must be an easier approach via CSS — and my hunch was right! All you need to do is go to the `.keypad > div.green` (and yellow and grey) and add `transition-delay: 1s;` — That’s it! :D

**Addressing CapsLock in handleKey**

At some point, I’ve noticed that if the CapsLock is on, it messes up the entered guess. To solve this, I added an if condition to catch that key, and ignore it (just return):

```
const useWordle = (solution) => {
  ...
  const handleKeyupVirtual = ( key ) => { 
    if (key === 'CapsLock') {
      return;
    }
    if(key === 'Enter'){
      ...
```

**Position the keyboard at the bottom of the page**

When the user is on the mobile, it’s more comfortable to have the keyboard lower on the screen (closer to the thumbs). The keyboard is currently right below the grid, which is closer to the center.

|![*Positioning the keyboard at the bottom is better for mobile users*](https://cdn-images-1.medium.com/max/2464/1*lFPVOWJohDJRrDVLRo2fpQ.png)|
|---|
|*Positioning the keyboard at the bottom is better for mobile users*|

With that said, I like that the keyboard is just below the grid when it’s the desktop view. This means we need different styles for different screen sizes!

We start by adding a `@media` rule with the condition of **max-width: 700px**, so it would affect only a narrow screen view. Inside that, we add the second version of the keypad style.

We need two modifications to make it work: First is adding to the keypad `position: absolute;` along with bottom, right, and left = 0. Then, we need to add to the body’s style `position: relative;` and set the height to be `90vh`.

If you set it to 100vh, it will look beautiful on your screen, in any size, but will require scrolling on the iPhone (I encourage you to check for yourself).
{% gist https://gist.github.com/cupofcodeblog/9977e71150643d8ecc401fa766fdb64e.js %}
Now, after all these modifications, we have a beautiful functioning keyboard:

|![The new keyboard — both pretty and smart!](https://cdn-images-1.medium.com/max/2000/1*yiI9BeIP3rylM44eS0C0XQ.png)|
|---|
|*The new keyboard — both pretty and smart!*|

---

## Post Game Modal

When the game is over, pops up a modal with some interesting information.

In the original game, there are statistics including the number of games played and the percentage of games won.

Below that, you can find past games’ guess distribution: How many times did the user guess the right word in how many tries.

Lastly, there is a share button that produces a string of squares, representing the game, that the user can share with their friends. We will create all those cool features!

![before and after](https://cdn-images-1.medium.com/max/2000/1*_I-GAUCrc1TBAQx5XiCl6Q.png)

### Add a Close Button

I’ve started with the thing that annoyed me most: I couldn’t close the modal. I had to add an exit button.

This is super easy to implement in pure HTML and JS but I wasn’t sure how to do it correctly in React. According to the base code, the modal shows up with the boolean `showModal`, and its value is set in the “Wordle” component:

### Add Close Button

I’ve started with the thing that annoyed me most: I couldn’t close the modal. I had to add an exit button. This is super easy to implement in pure HTML and JS but I wasn’t sure how to do it correctly in React. According to the base code, the modal shows up with the boolean `showModal` and its value is set in the “Wordle” component:
{% gist https://gist.github.com/cupofcodeblog/76e3d437cdc4bbb19ced174e130bf79c.js %}
Even if I add an X button, how do I change the value of that `showModal` boolean from *within* the Modal component? The trick is to pass `setShowModal` as a parameter to the component!
{% gist https://gist.github.com/cupofcodeblog/1066ade7a64633bfff24788dba13a8d0.js %}
Fun fact: `&times;` is a multiplication sign in HTML. It looks a bit different than “x”.

**Now the grid page is accessible post-game**

It’s important to mention that adding the functionality to close the modal means the user can see the grid of guesses after the game is over. This is fine, but we need to make sure he can’t type in any more guesses, both on the physical and virtual keyboards.

For the physical keyboard, we can simply add `else` to the “if-user-won” inside the useEffect, and only there add the keyup listener.
{% gist https://gist.github.com/cupofcodeblog/e70964797e1166fabb348763aad9ac1e.js %}
Please note that in the base code, there are different modal versions for winning and losing (and therefore different `if` conditions), but I merged them — so in my code, it’s one if condition: if the guess **is correct** or if it’s not correct but we **reached the 6th try** (`if (isCorrect || turn > 5)`)

For the virtual keyboard, we want to override the clickability of the keys on that last turn. Let’s see it in the code:
{% gist https://gist.github.com/cupofcodeblog/c1004cdfb0f589b7d4ffff9fb71f409d.js %}
We start by passing an argument notifying the keypad component that it should lock the keys. Considering we want to indicate the game is over, we can use the same boolean we use in the if-the-game-ended statement: `isLocked = (isCorrect || turn > 5)`. Now, the trick is to use `pointerEvents` property: We set it as “none” or “auto” based on the value of `isLocked`. Lastly, we add it to the style of the keyboard’s keys (you should have a total of 5 occurrences: top, middle, and bottom rows, Enter, and Backspace).

---

## Guess Distribution

Now we can move on to the coolest feature of the modal: Let’s show the user their statistics! In the total games they played, what was their guess distribution? This is divided into 2 parts: collecting the statistics, and presenting them.

### **Collect the** statistics

The base code doesn’t save how many guesses the player used, so we need to add that ourselves… but how?

We want this information to be saved even when the user closes the website. An easy way to do so is to use **local storage**.

Local storage’s stored data is saved across browser sessions, with no expiration time. So, if there is no statistics object at all — create one. Then, read the previous statistics, update them with this game’s result, and save them in the local storage.

**Important tip!** When coding with local storage, you need the ability to restart it, for testing purposes. I have this line of code commented out and easy to reach in App.js: `//localStorage.removeItem(“wordlestruckStatistics”);`. Comment it in, refresh, comment it out, refresh — and you get a clean slate.

Let’s write some code!

We will create an array of length 7, to represent the possible statistics: User guessed the solution after 1,2,3,4,5,6 tries, or they lost. 0 will represent the loss.

The tricky part is that when the user loses, the turn is set to 6 (because it was their 6th try), so we need to adjust the index based on whether the user was correct: `let index = isCorrect ? turn : 0;`

Now — where should the code sit? It can sit in the Modal component, or in the Wordle component. It’s best to update the statistics when the game is finished, so we will add our new code in the “If the game finished” condition in the Wordle component.
{% gist https://gist.github.com/cupofcodeblog/bb4aa1a1124d068142a6cdb18dbbb671.js %}


### Present the statistics

I encourage you to create a new React component dedicated to this section (`< Statistics />`) and call it from the Modal component.

First, you need to pull the data from localStorage. Then, save the number of total games in a variable. I present the data using a table element: The left column is the possible number of guesses (0–6), and the right column is the grey bar charts representing the distribution.

Now, those aren’t just any boring bar charts! In the original game, the distribution is written inside the bar, and the bar’s size depends on the distribution amount (but 0 isn’t empty). Also, the bar representing the number of guesses it took the user for the current game is colored green, not grey.

How do we achieve all that? For our bar charts, we’ll be using divs, so it’s a no-brainer to write in them. For the bar length, here is the math I did: `Math.floor((stat / totalGames) * 80) + 8`.

I divided stat-for-specific-guess-number by the total number of games to reach a proportional size. I multiplied by 80 because I didn’t want the biggest size to reach 100% width (but this is my personal preference). Lastly, I added 8 because I set the size of the 0 distribution to be 4, and I wanted 1 to be visibly bigger than that, even if there were many games played.

What about the green color? That property depends only on the turn number: `let barColor = (index === turn) ? "green" : "rgb(107, 107, 109)";`

Full code for the guess distribution section:
{% gist https://gist.github.com/cupofcodeblog/7d6d42de47c47ae2933c31e5a4aa0ec4.js %}
Screenshot:

![screenshot](https://cdn-images-1.medium.com/max/2000/1*-7axfTdBVbhsUjlGntM0Rg.png)

In the screenshot above you can see I also presented information on total games played and % of wins. After completing the guess distribution, this part will be easy for you to add by yourselves.

**Maybe we shouldn’t show the user his losses**

Later in the development, I decided I didn’t want to present the number of losses. This can be achieved by wrapping the code inside the `map` in `if(index!==0)`. Not the prettiest but the most minor change. If you do insist on prettier, there is another solution: Create a copy of `objStatistics` and `.shift()` it — this will remove the first item. There is no critical harm in shifting the original array, but it’s not a good practice because it can confuse the human eye reading this code. `objStatisticsWithout0` is not confusing anyone :)

```
// to remove first element - number of loses.
let objStatisticsWithout0 = objStatistics;
objStatisticsWithout0.shift();
```

If you choose to go this route, you’ll need another index variable: We shift our array, which means `array[turn]` does not match the guess distribution in `turn`. I called mine `guessDistIndex` and set it to be **index +1**.
{% gist https://gist.github.com/cupofcodeblog/91675b400140b7cff08f8e588bbcb3d6.js %}
Lastly, before we continue, I found it useful to make the modal scrollable. You can easily achieve that by adding in `index.css`:

```
.modal {
  ...
  overflow-x: hidden;
  overflow-y: auto;
}
```

**This is it for now. We will come back to the Modal after we go over the features that the rest of it depends on :D**

---

Ok, remember when I said that in a future blog post, I’ll show you how to give your Wordle a cool theme? In the next section, I expose the theme of my Wordle because I can’t hide it anymore — it will appear in the screenshots.

![meme2](https://cdn-images-1.medium.com/max/2000/1*t6NNpU74Aoge4Ush-d1pxQ.png)

My Wordle game is about Taylor Swift songs! The juicy part of a themed Wordle is creating the data set and adjusting the functionality to also work with solutions longer than 5 characters, and solutions containing more than one word. I will touch on those cool features in a future blog post, so stay tuned!

---

## The Landing Page

All the Wordle clone projects start the game with the main game page (the one with the grid and the keyboard), but the true Wordlers (*“Hi Wordler”*) among you know that there is a grey landing page with “How to play” and “Play” buttons. This is what we will work on in this section!

|![*The landing page*](https://cdn-images-1.medium.com/max/2000/1*pNdHKGfsQO2Ea8-cJ0TX-Q.png)|
|---|
|*The landing page*|

The structure of the page is pretty clear: Image, title, text, 2 buttons, more text. The tricky part is clicking the ***Play*** button and “moving between pages” while staying on the same page with the same URL, without routing!

We’ll start by creating a new component, which I called `<Welcome />`, and we add all the needed data there. You can present the date in this format by using:  

```
var today = new Date();
var displayDate = today.toLocaleDateString("en-US", options);
```

Calculating the index, on the other hand, is a tricky part which I will touch on later! Keep it hard-coded for now.

Adding this new component requires some refactoring because it sits between **App.js** and **Wordle.js**.
**App.js** used to call **Wordle.js**, but now it will call **Welcome.js**, and **Welcome.js** will call **Wordle.js**.

Now, we want the page to show the Wordle component if the button was clicked. This means the landing page content disappears and the Wordle content appears. We achieve that by using React’s [useState](https://react.dev/learn/updating-objects-in-state). That way we trigger a re-render to *replace* a value:

```
const [showWordle, setShowWordle] = useState(false)
```

So, on button click we set `showWordle` to be true, and we wrap the corresponding code sections so that they are dependent on the boolean value!

`{!showWordle && (<landing page code>)} {showWordle && (<Wordle component>)}`

Let’s see it in the Welcome component:
{% gist https://gist.github.com/cupofcodeblog/ebd8ebe44279f29e9e4da7e6db1d35b9.js %}
### The Instructions Modal

It’s time to address the second button on the landing page. The modal itself is nothing we haven’t already seen, but the tricky part is showing it above the grid page!

In the original game, clicking the ***how to play*** button leads you to the same page as the ***play*** button, just with the extra instructions modal. Once you click the ***X*** button you’ll see the grid and the keyboard.

|![*Wordlestruck’s instructions modal*](https://cdn-images-1.medium.com/max/2000/1*2Ypnc_3W3ecPPWy7TJbmHA.png)|
|---|
|*Wordlestruck’s instructions modal*|

So, you create a new component (`<Instructions />`) and put your code there. Where should you call it from? The structure will be similar to the ***landing page/wordle page*** structure, but from inside the wordle component!

We established earlier that both buttons will lead to the grid page, it’s just a question of whether or not the instructions modal will be shown on top. To achieve that, when we call the wordle component, we will pass a boolean parameter for the instructions page: `{showWordle && <Wordle solution={solution} showInstructions={showInstructions} />}`

That’s a good start, but it’s not enough. We also need to update the condition of showing the landing page vs showing the wordle component to take into consideration that new boolean:
{% gist https://gist.github.com/cupofcodeblog/77f1839f696edcf435d7e24e9c724218.js %}
Note that the initial state is to see the landing page, which means not showing the Wordle component, and not showing the instructions modal on top of the Wordle component. Then, if one of those booleans is set to true, we want to remove the landing page code and show the Wordle component.

So, we finished with the changes in the Welcome component, and we passed an additional parameter to the Wordle component. Let’s adjust the code to consider that new parameter!

**Inside the Wordle component:**

This time, different from the landing page logic, we don’t need to attach the Wordle code (grid, keyboard) to the `!showInstructionsModal` condition because we want it to exist underneath the instructions modal. That way, when we close the modal we will see the game!
{% gist https://gist.github.com/cupofcodeblog/19b5c394e6e92845804a296b6809f3b8.js %}
I didn’t show any code for the Instructions modal, because it’s easy to implement considering it’s not our first modal in the project. You can do it! :D

---

## Calculating the Game Number (Index)

At first glance, it may seem like the index is a tiny feature that appears only once on the landing page. In reality, this is a crucial part of this game, and it will be used throughout the whole game flow!

The index is accessed on the landing page, in the data set (to pull the word of the day), in the share button, and in various components to indicate if the user has played today or not. I know, most of those we didn’t do yet — which is why we need the index at this point!

When I started planning this function, I got overwhelmed: Every month has a different number of days, some years are leap years, and it even gets more complicated — **Date.now()** function returns the number of *milliseconds* since January 1, 1970, but there is no function to calculate the number of milliseconds since January 1, 1970, to another date that is not **now**!

At this point, I used the successful method “drop it for now and come back to it later”, and it worked! I googled it, understood all the parts (I can’t find the exact link but it was from [Geeks for Geeks](https://www.geeksforgeeks.org/)), and was determined to write it myself!

**How to calculate the number of days between two dates?**

First of all, the trick is not to calculate days *between* two dates, but to calculate the number of days in each date (# of days in 2023 years, # of days in 3 months, etc) and subtract the two!
{% gist https://gist.github.com/cupofcodeblog/70c9813e843a28bbd8ddb75b8db23daa.js %}
Note that when you calculate years and months, you don’t use the direct value, but subtract 1: If the date is March 13th, 2023, we don’t have all days of March, nor all days of 2023. This is why you see in the code above `year-1` and `month-1`. The for loop that is calculating full months in days is running from `0` to `month-1` (excluding), which matches the `monthDays` array: January’s amount of days is in cell number 0, not 1.

### Addressing Leap Years

The leap years aspect is not as complicated as it initially looks. A leap year is a year that has one additional day at the end of February. So, once you know how many years are leap years — that’s the amount of days you add.

What makes a year a leap year? According to Wikipedia,
>  *“This extra leap day occurs in each year that is an [integer](https://en.wikipedia.org/wiki/Integer) multiple of 4 (except for years evenly divisible by 100, but not by 400)”.*

In code, it simply means `if(year % 4 === 0 && !(year % 100 === 0 && year % 400 !== 0))`.

This is how I wrote the function:
{% gist https://gist.github.com/cupofcodeblog/58ef17c9ec45a2319114b524f87a9866.js %}
Don’t forget that leap years have their extra day in February, so keep the last year out of this calculation, and add a day only if the month is after February (`month > 2`).

Just to make sure we tackled all possible edge cases, let’s think about what would happen on Feb 28th and Feb 29th.

Feb 28th: `month` is not >2 then leap year doesn’t impact our calculation.

Feb 29th: `month` is not >2 then leap year doesn’t impact our calculation in this part. When we add the day to the sum of total days, we will add 29, so that’s ok.


**This caused a bug!**

I’ve discovered the hard way that Mac’s Safari and Firefox browsers do not agree to render the starting date in the current format. Chrome is more forgiving, which is why it took me a while to discover it.

The acceptable format is `new Date(2023,11,8)` — This means the month range is 0–11! (and only the month, not the other variables, I double-checked!)

It’s more human-friendly to address the months as starting from 1 so it’s better to adjust the month variable (and leave yourself a comment as a reminder!):

```
let startingDate = new Date(2023, 11, 8); // YYYY-(MM-1)-DD
...
let month = date.getMonth() - 1;
```

Now, you can optimize this by hard coding the start date. We set it only once, no need to calculate it again and again. I chose to keep the calculation because I haven’t launched my game yet, so the starting day is about to update.

If you are passionate about optimizing the program, you can calculate the first 2023 years in days only once and save those in variables! I won’t do it here, so let’s call this your homework ;)

Now that you have the game index calculated, you can modify the word pulling so that there will be only one word per day, based on the date!

The base code has the line: `const randomSolution = solutions[Math.floor(Math.random() * solutions.length)]`, which gives a new word every refresh of the page. If we change it to be `const todaysSolution = solutions[game_id]`, where `game_id` is the index calculated by our function, you could add the exciting sentence *“A new puzzle is released daily at midnight”* to your Instructions modal! :D

### Last but not least

We need to take a closer look at the index ranges: Let’s say we have 200 solution words. The solutions data set indexes will range between 0–199, because of the convention that an array starts at 0. The game indexes (numbers) should range between 1-infinity. This means we have a mismatch both at the start and at the end of the index ranges. Also, if we want the game index to start at 1, we need to revisit our **getGameIndexofToday()** function. We will fix it all in this section!

**App.js:**

This is our code at the moment:

```
const game_id = getGameIndexofToday(); // 0-infinity, but we don't want "game no.0"
...
const todaysSolution = solutions[game_id]; // 0-199
```

At some point, your game index will be higher than your dataset size. If you have 200 words in your data set, and it’s day 201, your app will crash. You can cover this case simply by adding `const solution_id = game_id % solutions.length;` instead of using the value coming from the index calculation function directly.

That is not enough. Once we fix `game_id` to start at 1, the first solution word will be the second item in the array (`solutions[1]`). We need to correct that! Therefore, we'll use `const solution_id = (game_id-1) % solutions.length;` instead.

**num_of_days_from_date.js:**

Lastly, let’s look at **getGameIndexofToday()** function. We built it so that we calculate amount of days in each date, and then subtract. What happens on the first day? The result will be 0, which is not a good starting index for our game (game number 0?). This happened because usually when we calculate something from start to finish, we exclude the end value. In our case, we need to include it. Let’s fix the code!

{% gist https://gist.github.com/cupofcodeblog/c257e7345433fd5a5a65317d0f09db3f.js %}

Not only do I add +1 before I return the value, but I make sure to rename the function to make it explicit that it includes the end day.

Now our indexes are matching!

```
const game_id = getGameIndexofToday(); // 1-infinity
...
const solution_id = game_id % solutions.length; // 1-200
const todaysSolution = solutions[solution_id]; // 1-200
```

---

This is a good place to close today’s tutorial. There are more features to add, but we’ll do them in the next blog post!

Let’s give a quick reminder of what we created today:

* A beautiful and functional virtual keyboard

* A cool statistics modal with the user’s guess distribution

* An instructions modal

* A landing page with the real date and game index!

This is a lot! But a lot of fun as well. 😀

In the next blog post, we will create the following features:

* The share functionality

* Different screens for whether the user played today or not

* Header buttons for statistics and instructions

Exciting news to all the swifties out there — my Wordlestruck game will launch at the end of this blog post series!

---

Blogging is my hobby, so I happily spend time and money on it. If you enjoyed this blog post, putting 1 euro in [my tipping jar](https://ko-fi.com/cupofcode) will let me know :)

Thank you for your support!

![[https://cupofcode.blog/](https://cupofcode.blog/)](https://cdn-images-1.medium.com/max/2000/0*tJ34Advmqvd2F7Jq.png)|
|---|
|[https://cupofcode.blog/](https://cupofcode.blog/)|