How I created a stack guessing tool using Next.js

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
Search for a command to run...

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
No comments yet. Be the first to comment.
Most of you know me for my consistency, a golden arrow in my blog series. I've written 1000 articles in 1008 days! Almost an article a day, and my honeymoon was the only holiday I ever took. I'm super proud of this achievement; it has been a fantasti...

It's not the first time I'll be talking about community. I think it's an essential aspect of any successful tool. This shows in my previous explorations of Astro, Medusa, and now Vendure as well. All these products thrive in a super open, welcoming, ...

The cool part about Vendure is how easy it is to set up and how abstract each layer is. Basically, we get the following elements: External database Server Worker Admin UI Frontend While this is amazing, it also brings a bit of complexity when it co...

The previous article looked at customizing Vendure on a data and process level. In this article, we'll look at customizing emails, as they are often a big part of a webshop system. We'll be looking at two different layers of customization for customi...

Even though Vendure is a pretty significant project out of the box, in some cases, we might want to go in and modify some elements to work to our specific use case. In this article, I'll take a high-level look at some elements we can customize within...

I'm all about creating funny tools just for the sole purpose of learning things.
In this article, I'll guide you through the process of creating this Stack guessing website I built in Next.js.
The stack guessing website works as follows:

Find your guilty pleasure stack
Let's start with the basics. We'll need a boilerplate Next.js application with the following dependencies:
I won't go into detail for each installation, but you'll get a detailed article by clicking on the above links.
Let's start by creating a new Next.js project:
npx create-next-app
Then navigate to the project you just created and add the following Tailwind related dependencies:
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
While we are here, let's initialize Tailwind.
npx tailwindcss init -P
And react icons as well:
npm i react-icons
Head over to your tailwind.config.js file and add the following to it:
module.exports = {
mode: 'jit',
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Now open the styles/global.css and modify so it looks like this:
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
Alright, so the homepage consists of a Next.js form where the user can input a first name.
Let's set up the HTML part for this.
import Head from 'next/head';
export default function Home() {
return (
<div className='p-8 min-h-screen bg-gray-100 flex'>
<Head>
<title>Find your guilty pleasure dev stack</title>
<meta name='description' content='Find your guilty pleasure dev stack based on your name' />
<link rel='icon' href='/favicon.ico' />
</Head>
<main className='flex flex-col justify-center h-auto w-full items-center rounded-xl border-4 border-white p-8'>
<h1 className='text-4xl mb-4 font-black'>Find your <span className='italic text-purple-300'>guilty pleasure</span> dev stack</h1>
<p className='mb-4 font-semibold'>We all know you got one 😏</p>
<form className='shadow-lg rounded-2xl p-6' onSubmit={findDevStack}>
<input name='name' placeholder='Enter your firstname' className='p-4 mr-2 rounded-xl' required autoComplete='name' />
<button type='submit' className='bg-purple-600 font-semibold text-white p-2 w-32 rounded-full hover:bg-purple-700 focus:outline-none focus:ring shadow-lg hover:shadow-none transition-all duration-300 m-2 uppercase'>
Find it
</button>
</form>
</main>
</div>
);
}
This will render a basic form. As you can see, we attached an onSubmit handler to the form.
Once submitted, the findDevStack method is called.
Once submitted, the page should redirect to http://ourwebsite/{first name} where the first name is dynamic.
import { useRouter } from 'next/router';
export default function Home() {
const router = useRouter();
const findDevStack = async (event) => {
event.preventDefault();
const name = event.target.name.value;
router.push(name);
};
return ();
}
Let's see how this looks:

Once submitted, we get redirected to /chris; however, this route does not exist yet.
As you can see above, this route can be any name.
To make this work in Next.js pages we need to use the [name].js format.
Go ahead and create the [name].js file in your pages directory.
This page needs to use the getStaticPaths function, but in our case, we want a blocking fallback to wait for our results.
export async function getStaticPaths() {
return { paths: [], fallback: 'blocking' };
}
We also want to use the getStaticProps function, which does the actual data loading.
export async function getStaticProps({ params }) {
const { name } = params;
const stack = await getStack(name);
return { props: { stack: stack, name: name } };
}
What we do here is extract the name from the parameters (URL). And call a function called getStack. We'll create this function in a bit.
Then we return the stack and the name to the actual page. The stack will be an array of items.
Let's first go ahead and create this getStack function.
I created a lib folder and placed a getStack.js file inside of it.
Then I defined an array of alphabet letters like so:
const devABC = {
a: {
title: 'Angular',
icon: 'DiAngularSimple',
},
b: {
title: 'Bootstrap',
icon: 'DiBootstrap',
},
// etc
};
And below that start the function export like so:
export default async function getStack(name) {
// Todo code
}
There are a couple of things we need to do.
name = name.toLowerCase().replace(/[^a-z]/gi, '');
Set methodconst nameSet = new Set(name);
const output = [...nameSet].map((abc) => devABC[abc]);
const output = [...nameSet].map((abc) => devABC[abc]).filter(Boolean);
return output;
Making the complete function look like this:
export default async function getStack(name) {
name = name.toLowerCase().replace(/[^a-z]/gi, '');
const nameSet = new Set(name);
const output = [...nameSet].map((abc) => devABC[abc]).filter(Boolean);
return output;
}
Head back over to the [name].js file and import this function in the top section of the file.
import getStack from '../lib/getStack';
Now we can work on the actual return of the page.
Let's start by importing the rest of the stuff we need.
import Head from 'next/head';
import * as Devicons from 'react-icons/di';
import Link from 'next/link';
Our page then again can have the stack, and name as properties like so:
export default function Name({ stack, name }) {
}
Let's set up the main render for this page:
export default function Name({ stack, name }) {
return (
<div className='flex min-h-screen p-8 bg-gray-100'>
<Head>
<title>Find your guilty pleasure dev stack</title>
<meta
name='description'
content='Find your guilty pleasure dev stack based on your name'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<main className='flex flex-col items-center justify-center w-full h-auto p-8 border-4 border-white rounded-xl'>
<h1 className='mb-4 text-4xl font-black'>
Hey <span className='italic text-purple-300'>{name}</span> this is it!
</h1>
<p className='mb-4 font-semibold'>Your guilty pleasure stack!</p>
<div className='p-4 mb-6 bg-gray-200 border-2 border-white shadow-lg rounded-xl'>
<ul>
{stack.map((item, index) => (
// Todo render a item
))}
</ul>
</div>
<Link href='/'>
<a className='italic underline'>Find your own stack</a>
</Link>
</main>
</div>
);
}
As you can see, I left out the render we are doing for each item.
Each item should show an icon and the name of the tool. As we need to render dynamic icons, I thought it would make more sense to extract this into it's own component like so:
export default function Name({ stack, name }) {
const DevItem = ({ item }) => {
const ICON = Devicons[item.icon];
return (
<li className='flex items-center text-xl'>
<ICON className='mr-2 text-4xl' />
{item.title}
</li>
);
};
return ()
}
That will dynamically load each icon and render it. We can then go back to our render and add a DevItem for each stack item.
<ul>
{stack.map((item, index) => (
<DevItem key={index} item={item} />
))}
</ul>
And there you go, we now get the stack rendered for a unique name like seen in the example!
You can also find the complete code on GitHub or view the result here.
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter