Opossum search

This notebook contains a simple example of generating code with the Gemini API and Gemini Flash. Just for fun, you’ll prompt the model to create a web app called “Opossum Search” that searches Google with “opossum” appended to the query.

The opossum image above is from Wikimedia Commons, and shared under a CC BY-SA 2.5 license.

Setup

Install the Google GenAI SDK

Install the Google GenAI SDK from npm.

$ npm install @google/genai

Setup your API key

You can create your API key using Google AI Studio with a single click.

Remember to treat your API key like a password. Don’t accidentally save it in a notebook or source file you later commit to GitHub. In this notebook we will be storing the API key in a .env file. You can also set it as an environment variable or use a secret manager.

Here’s how to set it up in a .env file:

$ touch .env
$ echo "GEMINI_API_KEY=<YOUR_API_KEY>" >> .env
Tip

Another option is to set the API key as an environment variable. You can do this in your terminal with the following command:

$ export GEMINI_API_KEY="<YOUR_API_KEY>"

Load the API key

To load the API key from the .env file, we will use the dotenv package. This package loads environment variables from a .env file into process.env.

$ npm install dotenv

Then, we can load the API key in our code:

const dotenv = require("dotenv") as typeof import("dotenv");

dotenv.config({
  path: "../.env",
});

const GEMINI_API_KEY = process.env.GEMINI_API_KEY ?? "";
if (!GEMINI_API_KEY) {
  throw new Error("GEMINI_API_KEY is not set in the environment variables");
}
console.log("GEMINI_API_KEY is set in the environment variables");
GEMINI_API_KEY is set in the environment variables
Note

In our particular case the .env is is one directory up from the notebook, hence we need to use ../ to go up one directory. If the .env file is in the same directory as the notebook, you can omit it altogether.

│
├── .env
└── examples
    └── Opossum_search.ipynb

Initialize SDK Client

With the new SDK, now you only need to initialize a client with you API key (or OAuth if using Vertex AI). The model is now set in each call.

const google = require("@google/genai") as typeof import("@google/genai");

const ai = new google.GoogleGenAI({ apiKey: GEMINI_API_KEY });

Select a model

Now select the model you want to use in this guide, either by selecting one in the list or writing it down. Keep in mind that some models, like the 2.5 ones are thinking models and thus take slightly more time to respond (cf. thinking notebook for more details and in particular learn how to switch the thiking off).

const tslab = require("tslab") as typeof import("tslab");

const MODEL_ID = "gemini-2.5-flash-preview-05-20";

Prompt the model to generate the web app.

const systemInstruction = `
  You are a coding expert that specializes in creating web pages based on a user request.
  You create correct and simple code that is easy to understand.
  You implement all the functionality requested by the user.
  You ensure your code works properly, and you follow best practices for HTML programming.
`;
const prompt = `
  Create a web app called Opossum Search:
  1. Every time you make a search query, it should redirect you to a Google search
  with the same query, but with the word opossum before it.
  2. It should be visually similar to Google search.
  3. Instead of the google logo, it should have a picture of this opossum:
  https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/Opossum_2.jpg/292px-Opossum_2.jpg.
  4. It should be a single HTML file, with no separate JS or CSS files.
  5. It should say Powered by opossum search in the footer.
  6. Do not use any unicode characters.
  Thank you!
`;
const response = await ai.models.generateContent({
  model: MODEL_ID,
  contents: prompt,
  config: {
    systemInstruction: systemInstruction,
  },
});
tslab.display.markdown(response.text ?? "");

Here’s the single HTML file for your “Opossum Search” web app, incorporating all your requirements:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Opossum Search</title>
    <style>
        /* Basic reset and body styling for a full-height page */
        body {
            display: flex;
            flex-direction: column;
            justify-content: space-between; /* Pushes main content to center, footer to bottom */
            align-items: center; /* Centers content horizontally */
            min-height: 100vh; /* Ensures body takes at least full viewport height */
            margin: 0;
            font-family: Arial, sans-serif; /* A common sans-serif font */
            background-color: #fff; /* White background like Google */
            color: #202124; /* Default text color */
        }

        /* Container for the main search elements (logo, form) */
        .container {
            flex-grow: 1; /* Allows container to expand and push footer down */
            display: flex;
            flex-direction: column;
            justify-content: center; /* Centers content vertically within its space */
            align-items: center; /* Centers content horizontally */
            width: 100%;
            max-width: 600px; /* Limits content width for a cleaner look, similar to Google */
            padding: 20px;
            box-sizing: border-box; /* Ensures padding is included in the width */
        }

        /* Styling for the opossum logo */
        .logo {
            width: 200px; /* Adjusted size for better visual balance */
            height: auto; /* Maintain aspect ratio */
            margin-bottom: 30px; /* Space below the logo */
        }

        /* Styling for the search form */
        .search-form {
            width: 100%;
            display: flex;
            flex-direction: column;
            align-items: center; /* Centers items within the form */
        }

        /* Styling for the search input field */
        .search-input {
            width: 100%;
            max-width: 584px; /* Approximate width of Google's search bar */
            height: 44px; /* Standard height for inputs */
            padding: 0 14px;
            border: 1px solid #dfe1e5; /* Light grey border */
            border-radius: 24px; /* Rounded corners */
            font-size: 16px;
            box-shadow: 0 2px 5px 1px rgba(64,60,67,.16); /* Subtle shadow for depth */
            outline: none; /* Remove default focus outline */
            transition: box-shadow 0.3s ease; /* Smooth transition for shadow on focus */
        }

        .search-input:focus {
            box-shadow: 0 3px 8px 1px rgba(64,60,67,.24); /* Stronger shadow on focus */
        }

        /* Container for the search buttons */
        .button-container {
            display: flex;
            justify-content: center;
            margin-top: 20px; /* Space above buttons */
        }

        /* Styling for the search button */
        .search-button {
            background-color: #f8f9fa; /* Light grey button background */
            border: 1px solid #f8f9fa; /* Matching border */
            border-radius: 4px; /* Slightly rounded corners */
            color: #3c4043; /* Dark grey text */
            font-family: Arial, sans-serif;
            font-size: 14px;
            margin: 11px 4px; /* Spacing around buttons */
            padding: 0 16px; /* Padding inside buttons */
            line-height: 27px;
            height: 36px; /* Button height */
            min-width: 54px;
            text-align: center;
            cursor: pointer; /* Pointer on hover */
            user-select: none; /* Prevent text selection */
            transition: background-color 0.3s, border-color 0.3s; /* Smooth transitions */
        }

        .search-button:hover {
            border: 1px solid #dadce0; /* Darker border on hover */
            box-shadow: 0 1px 1px rgba(0,0,0,.1); /* Subtle shadow on hover */
            background-color: #f8f9fa; /* Background remains consistent on hover */
        }

        /* Styling for the footer */
        .footer {
            width: 100%;
            text-align: center;
            padding: 15px 0;
            font-size: 13px;
            color: #70757a; /* Muted text color */
            border-top: 1px solid #dadce0; /* Subtle line above the footer */
            background-color: #f2f2f2; /* Light grey background for footer */
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- Opossum Logo -->
        <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/Opossum_2.jpg/292px-Opossum_2.jpg" alt="Opossum Logo" class="logo">

        <!-- Search Form -->
        <form class="search-form" id="searchForm">
            <input type="text" class="search-input" id="searchInput" placeholder="Search Opossum..." autocomplete="off">
            <div class="button-container">
                <button type="submit" class="search-button">Opossum Search</button>
            </div>
        </form>
    </div>

    <!-- Footer -->
    <div class="footer">
        Powered by opossum search
    </div>

    <script>
        // Get a reference to the search form
        document.getElementById('searchForm').addEventListener('submit', function(event) {
            event.preventDefault(); // Prevent the browser's default form submission

            // Get the value from the search input field
            const searchTerm = document.getElementById('searchInput').value;

            // Do not redirect if the search term is empty or just whitespace
            if (searchTerm.trim() === '') {
                return;
            }

            // Prepend "opossum " to the user's query
            const prefixedSearchTerm = 'opossum ' + searchTerm;

            // Encode the search term for use in a URL (handles spaces and special characters)
            const encodedSearchTerm = encodeURIComponent(prefixedSearchTerm);

            // Construct the Google search URL
            const googleSearchUrl = 'https://www.google.com/search?q=' + encodedSearchTerm;

            // Redirect the user's browser to the constructed Google search URL
            window.location.href = googleSearchUrl;
        });
    </script>
</body>
</html>
const codeBlock = response.text?.match(/```html\n([\s\S]*?)\n```/);
if (!codeBlock || codeBlock.length < 2) {
  throw new Error("No HTML code block found in the response");
}
const htmlCode = codeBlock[1].trim();
tslab.display.html(`
<div style="border: 1px solid black; border-radius: 5px;">
  ${htmlCode}
</div>
`);
Opossum Search