In this tutorial, we will build an AI UI Generator using Next.js and the Gemini API. It is similar to v.0 by Vercel which allows you to generate UI with a simple prompt and use it in your web apps.

This type of dynamic code generation can be the beginning of a new paradigm in software development where User Interfaces are generated by AI according to user’s needs. This concept is being termed as “Generative UI”.

Here’s an example of how our application will work: Watch the video tutorial here: https://www.youtube.com/watch?v=IFQI3t89eO8

Let’s begin by creating a new Next.js project.

Step 1: Create a new Next.js project

npx create-next-app@13.4 ui-generator

You will be prompted to select settings for the new project, make sure to follow these choices:

  • For TypeScript, select No because this tutorial uses JavaScript.

  • Feel free to choose any option for enabling ESLint.

  • Choose Yes for Tailwind CSS.

  • Exclude the src/ directory by selecting No, which is the default option.

  • Enable App Router by selecting Yes, which is the recommended option.

  • Lastly, select No for import alias configuration.

Change the working directory to your new Next.js project, ui-generator and run the application to make sure everything is fine:

cd ui-generator
npx run dev

Step 2: Building the Frontend

In the new app router in Next.js, app/page.js hosts the code for the home page. Remove everything inside it apart from the outer divs, and now we have an empty canvas to create anything we want.

Create a simple interface for this application which includes an input box to enter a prompt, a div placeholder for the code to be generated, and button to copy code:

'use client'

export default function Home() {

  return (
    <main className="flex min-h-screen flex-col items-center p-12">
      <div className="max-w-5xl w-full flex-col items-center justify-center text-sm lg:flex">
        <p className='text-3xl mb-4 font-bold text-gray-600'>UI Generator</p>
        <div className="relative flex items-center w-full h-12 rounded-lg focus-within:shadow-md bg-transparent overflow-hidden">
          <input
            className="peer h-full w-full outline-none bg-gray-100 text-sm text-gray-700 pl-4 pr-12"
            type="text"
            placeholder="Enter a prompt..."
          />
          <div onClick={handleGenerate} className="absolute right-0 flex items-center justify-center h-full w-12 bg-blue-500 hover:bg-blue-600 text-white cursor-pointer">
            <p className='text-2xl'>âž”</p>
          </div>
        </div>

        <div className='flex'>
          <button className='bg-blue-500 text-white px-5 py-3 mt-7 hover:bg-blue-600 m-2' onClick={handleCopy}>Copy Code</button>
        </div>

        <div className='bg-transparent border-2 border-gray-300 rounded mt-7 p-2 flex justify-center'>
          {/* Generated code */}
        </div>

      </div>
    </main>
  )
}

Step 3: Obtain the Gemini API Key

Before we start working on our back-end of the application, we need to obtain the Gemini API key to be able to leverage Google’s generative AI tools.

Go to https://ai.google.dev/aistudio to get the Gemini API key for free.

Create a .env file in the root of your project and store your API key:

GEMINI_API_KEY=[YOUR API KEY]

Step 4: Create server-side function to handle POST Requests

Install the Google AI JavaScript SDK enables you to use Google’s generative AI models. Make sure you are in your project’s root directory and run the following command in your terminal:

npm install @google/generative-ai

In Next.js app router, you can define custom route handlers in route.js file inside the app director. For example the file app/api/generate/route.js maps to /api/generate route.

Let’s create a function to handle POST requests in /api/generate/route.js :

import { GoogleGenerativeAI } from '@google/generative-ai';
import { NextResponse } from 'next/server';

export async function POST(req, res) {
    const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY)

    try {
        const model = genAI.getGenerativeModel({ model: "gemini-pro" });

        const data = await req.json()
        const prompt = data.body + ". Write jsx code and use tailwindcss for modern UI. Don't make any imports. Only output code.";

        const result = await model.generateContent(prompt);
        const response = await result.response;
        const code = await response.text()

        return NextResponse.json({code: code})
    } catch (error) {
        console.error(error)     
    }
}

Here, we create an instance of GoogleGenerativeAI from the API key we stored in the .env file, define a model that uses gemini-pro model.

We retrieve the JSON data from the request body and store it in a variable data. But we don’t directly pass it as a prompt to the Gemini API. Instead, we add some “prompt injection”, i.e. additional instruction for the AI model so that we can have control over the output we get.

Then we pass the prompt to Gemini using the generateContent() method. Finally we pass the generated output as a JSON response object.

Step 5: Create a client side function to handle POST request

First, we will define two state variables in our home page page.js :

const [prompt, setPrompt] = useState('');
const [code, setCode] = useState('');

Set an onChange listener to bind the state variable prompt to the input text:

<input
  className="peer h-full w-full outline-none bg-gray-100 text-sm text-gray-700 pl-4 pr-12"
  type="text"
  value={prompt}
  onChange={(e) => setPrompt(e.target.value)}
  placeholder="Enter a prompt..."
/>

Create a function called handleGenerate() above the return function:

const handleGenerate = async () => {
    try {
      const response = await fetch('/api/generate', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ body: prompt })
      });

      const data = await response.json();
      if (response.ok) {
        setCode(data.code);

      } else {
        console.error(data.error);
      }
    } catch (error) {
      console.error('Error:', error);
    }
  };

This function initiates an HTTP POST request to the endpoint /api/generate using the fetch function. It sends the prompt as a request object.

Set an onClick listener in your send button to execute the handleGenerate function:

<div onClick={handleGenerate} className="absolute right-0 flex items-center justify-center h-full w-12 bg-blue-500 hover:bg-blue-600 text-white cursor-pointer">
    <p className='text-2xl'>âž”</p>
</div>

We need to display the code which is stored in the code state variable somewhere:

<div className='bg-transparent rounded mt-7 p-2 flex justify-center'>
    {code}
</div>

Now test the application by running npm run dev and test the application by entering a prompt such as “create a pricing section for my website”.

Step 6: Rendering Generated JSX Code

Now the final piece of the puzzle is rendering the generated code into user interface instead of just displaying the code. For that, we will use a library called html-react-parser .

Open a new terminal, change directory to the root of your project and run:

npm install html-react-parser

To use html-react-parser , in your page.js make the following import:

import parser from 'html-react-parser';

Now use the parse method to convert String into JSX code:

<div className='bg-transparent rounded mt-7 p-2 flex justify-center'>
    {parse(code)}
</div>

Step 7: Function to Copy code

You can write a function to copy code:

const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(code);
      alert("Code copied to clipboard")
    } catch (err) {
      console.error('Failed to copy: ', err)
    }
  }

It uses the navigator.clipboard.writeText method to write the text stored in the variable code to the system clipboard.

Wrapping Up

You can get the full code for this project at the github repo: https://github.com/avionmission/ui-generator