Building a NextJS ChatGPT UI Categorizer Unveil the power of NextJS 13 and ChatGPT API to categorize UI components into Atomic Design terms, all achievable in under 30 minutes! 2023 In our team, while working on a prototype, we wanted to experiment with Atomic Design in frontend development. If you're scratching your head thinking, "What's Atomic Design?", let me break it down for you: Atomic Design is a methodology that breaks down interface design into five hierarchical stages:Atoms: These are the smallest building blocks like buttons, input fields, or colors.Molecules: Assemblies of atoms functioning as a unit, like a search bar combining an input field and a button.Organisms: Groups of molecules joined together to form a distinct section of an interface, like a header.Templates: These are page-level objects that place components into a layout but devoid of any real content.Pages: The highest level that focuses on content, variations, and data connections within the template.Why is Atomic Design even good? Well, it helps you create scalable and maintainable design systems. Think of it like Lego blocks; each piece (or stage) is essential and serves a purpose. It makes component management easier, keeps your team on the same page, and most importantly, makes it simple to identify reusable components. ๐But, here's where we hit a snag. We kept asking ourselves, "Where does component 'x' go? Is it an atom or a molecule?" That's when it hit meโwhy not consult ChatGPT? Maybe it can streamline this categorization for us. ๐So, I built an app that sends a prompt to ChatGPT, parses the response, and displays the category to the user. Simple, right? The app is now live and you can check it out at https://atomic.aien.me. But if you're keen on the nitty-gritty details of how it was implemented, stick around. I'm going to walk you through it, step-by-step.Setting Up the Basics ๐Before diving into the code, let's make sure you've got the basics covered:Node.js and npm are installed (I prefer using nvm)A Next.js 13 project up and runningAn OpenAI API key for ChatGPT (Click here to build yours)Install Dependencies ๐คStart by installing Axios for making HTTP requests:$ yarn add --dev --exact prettier $ yarn add --dev eslint-config-prettierAnd change the .eslintrc.json accordingly:{ "extends": ["next", "prettier"] }You must also add prettier script to your package.json file:... "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "format": "prettier . --write" }, ...Directory Structure ๐Before we dive into the code, let's talk about how I've laid out my directories and files. While you can structure yours in a way that suits you, here's a guide to what I've done:โโโ .env # Environment variables โโโ .env.local # Local environment overrides โโโ .eslintrc.json # ESLint configuration โโโ .gitignore # Git ignore rules โโโ .prettierignore # Files to ignore during prettification โโโ .prettierrc.json # Prettier configuration โโโ README.md # Project README โโโ next-env.d.ts # Next.js environment types โโโ next.config.js # Next.js configuration โโโ package-lock.json # npm dependency versions โโโ package.json # npm package and script info โโโ postcss.config.js # PostCSS configuration โโโ src # Source code folder โ โโโ app # App specific components and logic โ โ โโโ api # Backend API logic โ โ โ โโโ chat # Chat API routes โ โ โ โโโ route.ts # Route handling for chat โ โ โโโ globals.css # Global styles โ โ โโโ layout.tsx # App layout component โ โ โโโ page.tsx # Main page component โ โโโ types # TypeScript types โ โโโ index.ts # Main type definitions โโโ tailwind.config.ts # Tailwind CSS config โโโ tsconfig.json # TypeScript config โโโ yarn.lock # Yarn lock file Key Points:.env and .env.local: These files hold your environment variables. The .local version is for your local dev setup and typically not committed to version control.src/app/api/chat/route.ts: This is where our ChatGPT API handler lives. It receives a UI component name, sends it to ChatGPT, and returns the Atomic Design category.src/app/globals.css: Any global CSS styles go here. Keeps things neat.src/types/index.ts: This is where I define any TypeScript types that I'll be using throughout the app.Feel free to adapt this directory structure to fit your project's needs. A well-organized folder structure makes the development process much more pleasant, trust me. ๐คCreating the Next.js API Handler ๐The cornerstone of this application is the Next.js API handler. This function serves as the intermediary that receives a component name, queries it against the ChatGPT model, and subsequently returns the appropriate Atomic Design categorization for the specified component.import { ChatApiResponse } from "@/types"; import { NextRequest, NextResponse } from "next/server"; export async function POST( request: NextRequest, ): Promise<NextResponse<ChatApiResponse>> { const { input } = await request.json(); if (!input) return NextResponse.json({ error: "error, input not defined" }); const chatGPTApiUrl = "https://api.openai.com/v1/chat/completions"; const apiKey = process.env.API_KEY; const model = "gpt-3.5-turbo"; const payload = { messages: [ { role: "user", content: "I will put this a bit later", }, ], model, }; const headers = { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }; try { const response = await fetch(chatGPTApiUrl, { method: "post", body: JSON.stringify(payload), headers, }).then((r) => r.json()); return NextResponse.json({ output: JSON.parse(response.choices[0].message.content), }); } catch (e: any) { return NextResponse.json({ error: "error, input not defined" }); } }Let's break down this code snippet piece by piece in a manner that's easy to understand.Import Statementsimport { ChatApiResponse } from "@/types"; import { NextRequest, NextResponse } from "next/server"; ChatApiResponse: This is a TypeScript type we've defined to structure the API response.NextRequest, NextResponse: These are imported from Next.js and are used to type our API request and response.The POST Function โ๏ธexport async function POST( request: NextRequest, ): Promise<NextResponse<ChatApiResponse>> { We define an asynchronous function named POST that takes a NextRequest object and returns a Promise with a NextResponse of type ChatApiResponse.Parsing the Request ๐const { input } = await request.json(); We destructure input from the incoming JSON request payload.Error Handling โผ๏ธif (!input) return NextResponse.json({ error: "error, input not defined" }); If input is not defined, we return an error response.ChatGPT API Setup ๐const chatGPTApiUrl = "https://api.openai.com/v1/chat/completions"; const apiKey = process.env.API_KEY; const model = "gpt-3.5-turbo"; Here we define the URL for the ChatGPT API, the API key from environment variables, and the model we want to use.Payload and Headersconst payload = { /* ... */ }; const headers = { /* ... */ }; payload contains the message that will be sent to ChatGPT. headers contain the authorization and content type information for the API request.API Request and Response Handling โ๏ธtry { // API request code here return NextResponse.json({ output: JSON.parse(response.choices[0].message.content), }); } catch (e: any) { return NextResponse.json({ error: "error, input not defined" }); } We use a try-catch block to handle the API request and response. The try block sends a request to ChatGPT and returns the parsed output. The catch block returns an error response if something goes wrong.Talking to ChatGPT ๐ฃ๏ธFirst, I used a simple prompt to ask ChatGPT to categorize the component:Using Atomic Design Principles, categorize the following UI component into one of the Atomic Design terms: Atom, Molecule, Organism, Template, Page, Unknown. The component name is: [component_name_here]However, it didn't actually gave me enough information. So after experimenting a bit with the prompt, here's what I ended up with which I set for the payload.messages[0].content:Using Atomic Design Principles, categorize the following UI component into one of the Atomic Design terms: - Atom ๐งช - Molecule โ๏ธ - Organism ๐ - Template ๐ - Page ๐ - Unknown โ. The component name is: "${input}". The output must always be a json with following format, at any cost: { type: [the type], component: [what is the purpose of ${input} in UI. Why is it categorised as it is.] items: [an string array of possible components that can together make ${input} including their type in paranthesis in the format of "component name (component type)"] } In case of errors or that the input anything other than a component, write the single text "The component '${input}' does not fit into any of the Atomic Design terms."Type checking ๐ฆบAfter establishing the prompt and what the response should look like, the next step is to define a TypeScript interface for the ChatGPT API response. Here's how:export interface ChatApiResponse { output?: { type: string; component: string; items: string[]; }; error?: string; } Let me break it down for you:output: This optional field houses the categorized UI component's details.type: Indicates which Atomic Design category the UI component falls under.component: Gives a quick description of why the UI component is categorized the way it is.items: This is an array of strings, indicating the components that can collectively create the UI component in question. Each item also shows the type of component in parentheses.error: An optional string field that will hold any error messages, if they arise.This TypeScript interface ensures that any response from the ChatGPT API fits into a predefined structure, which can be easily manipulated or displayed within your Next.js components. ๐คAbsolutely, let's hop right into the UI development using React. ๐จCrafting the UI with React ๐ ๏ธOur user interface is built with React and TypeScript. The purpose is to create an interactive UI where a user can input a component name and get its Atomic Design category. Letโs break down what's happening in the code.The Imports and State Management ๐งณFirst, we're importing essential modules and types:"use client"; import { ChatApiResponse } from "@/types"; import { FormEvent, useState } from "react"; ChatApiResponse: Our own type definition to make sure the server response matches what we expect.FormEvent, useState: Hooks and events from React to manage our form and state.State Variables ๐ฆWe have two state variables:response to store the server's answer.loading to toggle a loading state.const [response, setResponse] = useState<ChatApiResponse>(); const [loading, setLoading] = useState<boolean>(false); Handling Form Submission ๐We define an async function handleSubmit that gets triggered when the form is submitted.const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { // ... the function's body }; It fetches the data from our Next.js API and sets the response into state variables. Additionally, it toggles the loading state.The JSX Structure ๐๏ธFinally, we render our form and display the response.return ( <main className="grid h-screen place-items-center"> {/* ... rest of the JSX */} </main> ); Form ๐The form comes with an input field for the component name and a submit button. On submission, it triggers the handleSubmit function.const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); const data = new FormData(e.currentTarget); const input = data.get("input"); setLoading(true); const r: ChatApiResponse = await fetch("/api/chat", { method: "post", body: JSON.stringify({ input, }), }).then((r) => r.json()); setResponse(r); setLoading(false); }Response Display ๐ฅ๏ธWe display the Atomic Design type, its purpose, and the components that can form it, all conditionally rendered based on whether data has been loaded or not.{loading ? ( <p className="mt-5 text-center">Loading... please wait</p> ) : ( <> {/* ... the display logic for response */} </> )} And that wraps up our user interface! With this setup, you have a solid foundation for categorizing UI components according to Atomic Design principles. ๐The response part<> <p className="mt-5 text-center"> {response?.output?.type || 'Write "button" as an example.'} </p> <p className="mt-2">{response?.output?.component}</p> <p className="mt-2"> {(response?.output?.items.length && response?.output?.items.length > 0 && `It is usually made of:`) || ""} </p> <ul className="list-disc p-5"> {response?.output?.items.map((i, idx) => ( <li key={idx}>{i}</li> ))} </ul> </>Feel free to adjust the UI as per your brand's design guidelines or functional requirements. ๐Testing the App ๐งชRunning some tests proved that the app works like a charm! I sent in a variety of UI components and ChatGPT categorized them perfectly.Absolutely, let's seal the deal with a compelling conclusion! ๐ฏWrapping Up ๐Building this ChatGPT-powered app was not just a coding exercise; it was a doorway into how we can blend the realms of design and automation. Itโs a tiny cog in the machine of design innovation, but its impact is sizable when it comes to expediting the design process. ๐The atomic design classification issue we tackled is just the tip of the iceberg. There's a vast sea of possibilities when you think about what else could be automated or enhanced through machine learning and natural language processing. ๐๐กWhat's Next? ๐ฃ๏ธGoing forward, I plan to add more features, such as batch processing of components and integrating more design frameworks. And who knows? Maybe soon, we'll have a full-blown design assistant, courtesy of ChatGPT and some React magic. ๐งโโ๏ธ๐ซThank You for Stopping By! ๐I truly hope you found this walk-through enlightening and that it sparked some ideas for you to explore. If you integrate ChatGPT into your own projects, I'd love to hear how it goes! Feel free to reach out and share your experiences. ๐Don't forget to check out the app at https://atomic.aien.me and let the atomization begin! ๐Until next time, keep coding, keep designing, and keep pushing the boundaries of what's possible. ๐ช๐จCheers to bridging the gap between design and code! ๐ฅ Comments You may be interested 25 Jul, 2026 Most days I don't write code anymore ๐ฏ Personal Experience ๐ฎ Technology Commentary Read 21 Jul, 2026 Important Tips for Building Your First Game Read Load More
25 Jul, 2026 Most days I don't write code anymore ๐ฏ Personal Experience ๐ฎ Technology Commentary Read