Let's go through how everything here works.
I'm using NextJs and Javascript. If you want to use Typescript, you can see react-basics-ts. To set up your Nextjs project, run the following command:
npx create-next-app@latest
You will be asked a few questions:
What is your project named? my-app
Would you like to use TypeScript? No
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like your code inside a 'src/' directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to use Turbopack for 'next dev'? No / Yes
Would you like to customize the import alias ('@/*' by default)? No / Yes
After that, run the following to run the development server.
npm run dev
// If you're using React, use the following instead:
npm start
Open the development server at localhost:3000.
Great π! You have now set up your own NextJs project! If you want to use the styles on this website, you can go here.
Let's talk about counters. They usually appear when you create a Vite + React project, like shown below, as they are the simplest things you can do with React.
Let's go to our app.js
, delete everything and create a default function.
export default function Page(){
return(
<>
Hello world
</>
)
}
Now, save your changes and go to the devlopment server.
Pardon the bad quality π
. Now we have a simple 'Hello world' on our server. To create our counter, we'll import useState
from React.
import { useState } from 'react';
Don't forget to add "use client"
at the top of your project. Not doing that would render an error.
"use client"
Great, now paste the following code in your app.js
:
"use client"
import { useState } from 'react';
export default function Page(){
const [ count, setCount ] = useState(0);
return(
<>
<button onClick={setCount(count + 1)}>Count is {count}.</button>
</>
)
}
Huh � Okay, I'll break this code into pieces.
const [ count, setCount ] = useState(0);
Okay, so we're defining a variable in the code above, and setting the value to useState(0)
. UseState is just something from React that allows you to change a variable.
<button onClick={setCount(count + 1)}>Count is {count}.</button>
So now when you press the button, we're setting count
to count
+ 1. The count
variable was previously 0, so 0 + 1 = 1. What about the reset button? Okay, let's add a button into our app.js
and give it this function:
"use client";
import { useState } from "react";
export default function Page() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={setCount(count + 1)}>Count is {count}.</button>
<button onClick={setCount(0)}>Reset</button>
</>
);
}
Right now, in out reset button, we're setting the count
variable to 0, which is reseting it. Let's test our project now, and...
...what? We just had an error. Let's see. Hmm. Okay, that's because we didn't put the setCount(count + 1)
inside a function. To add it in a function, simple put it in {}
and add a () =>
in front of it.
"use client"
import { useState } from "react";
export default function Page() {
const [ count, setCount ] = useState(0)
return(
<>
<button onClick={() => {setCount(count + 1)}}>Count is {count}.</button>
<button onClick={() => {setCount(0)}}>Reset</button>
</>
)
}
Now let's run the development server again.
Nice! Now we have a simple counter.
How do modals work? Well, again, we will have to use useState
. We need a trigger (text on the button), and some children. Let's go and create a new file at src/components/Modal.js
. Next, we'll export a default function:
export default function Modal(){
return(
<p>Modal</p>
)
}
Now, let's import it to our src/app/page.js
.
import Modal from "@/components/Modal";
export default function Page(){
return(
<>
...
<Modal></Modal>
</>
)
}
Nice! Now our modal component just shows the text: Modal Let's import useState
now and define open
and setOpen
.
"use client"
import { useState } from "react";
export default function Modal() {
const [ open, setOpen ] = useState(false)
return(
...
)
}
Great! Let's create some functions:
...
export default function Modal() {
...
function HandleClose(){
setOpen(false);
}
function HandleOpen(){
setOpen(true);
}
}
So now let's add the trigger button:
...
export default function Modal({ trigger, children }){
...
return(
<button onClick={...}>{trigger}</button>
)
}
When we press that trigger button, we want our modal to appear. But how do we do that? Remember we created the functions handleClose
and handleOpen
? Let's use handleOpen
since we're opening the modal.
<button onClick={handleOpen}>{trigger}</button>
Wait, before you do that, you will need to give the Modal some props otherwise it will not know which variable is trigger
since there are no definitions of trigger
. You can do it by:
...
export default function Modal({ trigger, children }){
...
return(
...
{(open) && (
...
)}
)
}
Oh, one more thing... {children}
is a variable that is understood by React as the content inside the component.
Now you're probaly guessing that we would render a <div>
if open
is true. Correct! Whatever inside {}
is Javascript or Typescript. The following code shows you how to do it:
...
export default function Modal({ trigger, children }){
...
return(
<>
...
{(open) && (
<div></div>
)}
</>
)
}
Now, when you click on the trigger button, it will show nothing. That's because you did not put any thing in the <div>
. Let's give it some styles so that the modal goes to the center and has some backgroud shadow:
...
export default function Modal({ trigger, children }){
...
return(
<>
...
{(open) && (
<div
style={{
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
background: "rgba(0, 0, 0, 0.5)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{children}
</div>
)}
</>
)
}
Let's try it out!
Nice, but there's no close button. Let's add one.
...
export default function Modal({ trigger, children }){
...
return(
<>
...
<div style={{...}}>
{children}
<button onClick={handleClose}>Close</button>
</div>
</>
)
}
Now you'll realize that the close button is just beside your content. That's because we didn't put it in a footer
. Let's try doing that.
...
export default function Modal({ trigger, children }){
...
return(
<>
...
<div style={{...}}>
{children}
<footer>
<button onClick={handleClose}>Close</button>
</footer>
</div>
</>
)
}
Well, that seemed not to work. That's because we do not have a maximum width. There's also no borders. We'll give it extra styling by giving it and id of modalContent (you don't need to do that).
...
export default function Modal({ trigger, children }){
...
return(
<>
...
<div id="modalContent">
{children}
<footer>
<button onClick={...}>Close</button>
</footer>
</div>
</>
)
}
Let's edit our src/app/globals.css
. You can do so by downloading our style pack.
#modalContent {
min-width: 20%;
min-height: 20%;
padding: 10px;
border-radius: 15px;
background-color: light-dark(var(--light-bg), var(--dark-bg));
}
Finally, our src/components/Modal.js
should look like this:
"use client";
import { useState } from "react";
export default function Modal({ trigger, children }) {
const [open, setOpen] = useState(false);
function handleClose() {
setOpen(false);
}
function handleOpen() {
setOpen(true);
}
return (
<>
<button onClick={handleOpen}>{trigger}</button>
{open && (
<div
style={{
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
background: "rgba(0, 0, 0, 0.5)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<div id="modalContent">
{children}
<footer>
<button onClick={handleClose}>Close</button>
</footer>
</div>
</div>
)}
</>
);
}
β¨ Here's a sneak peek on what your modal should look like:
We know that you can't wait, but we can't rush through thisπ¨ IMPORTANT π¨ process. Please wait enough until NEW π₯ stuff comes out. In the meantime, please fork our project here...