teachyou.ai academy
← All posts
Codex

OpenAI Codex for Frontend Prototyping

Ira Menon · May 18, 2026 · 14 min read

Why Frontend Prototyping Is a Different Problem Than Frontend Engineering

Most articles about AI coding tools treat "writing code" as one big undifferentiated task. It isn't. Prototyping a frontend and engineering a production frontend are different jobs with different success criteria, and if you use the same workflow for both, you either over-engineer a throwaway demo or under-engineer something that ships to real users.

A prototype exists to answer a question fast: does this layout make sense, does this interaction feel right, can a stakeholder click through this and say "yes, build it"? Nobody cares about test coverage or bundle size at this stage. What matters is speed of iteration — how quickly you can go from "what if the dashboard looked like this instead" to a clickable screen.

This is exactly the environment where OpenAI Codex, accessed through its CLI, earns its keep. Codex is a coding agent that can read a repository, plan a change, write files, run commands, and iterate against feedback — all from your terminal. For frontend prototyping specifically, that means you can describe a component or a whole page in plain English and get back working React, Vue, or plain HTML/CSS/JS that you can immediately preview, tweak, and throw away if it doesn't work.

This article is a practical walkthrough of how to actually use Codex CLI for this purpose: setting it up, structuring your prompts, running a real prototyping loop, handling the rough edges, and knowing when to graduate a prototype into real production code. It assumes you already write frontend code and want a faster first draft, not a magic replacement for frontend skill.

Getting Codex CLI Installed and Ready

Codex CLI ships as an npm package, which makes it trivial to get running alongside a typical frontend toolchain (Node, npm/pnpm, Vite, whatever you already use).

npm install -g @openai/codex
codex login

Once installed, running codex from inside a project directory drops you into an interactive session. Codex reads your current working directory as context — it can see your package.json, your existing components, your CSS conventions — which matters a lot for prototyping because you want new screens to at least loosely match the visual language you already have, not look like they were airlifted in from a different app.

A few settings worth configuring before you start prototyping seriously:

codex --model gpt-5-codex
codex --approval-mode suggest

The --approval-mode flag controls how much Codex does before asking you to confirm. For prototyping, suggest mode (where it proposes file changes and you approve them) is usually the right default — you want to see the diff before it lands, especially early on while you're still building trust with the tool's output. Once you're comfortable, auto-edit mode lets it write files directly and only prompts you for potentially destructive shell commands, which speeds up the loop considerably.

It's also worth creating a small AGENTS.md file at your project root — Codex reads this automatically for repo-specific conventions, similar to how other coding agents read a memory file:

# AGENTS.md

- Framework: React 18 + Vite + TypeScript
- Styling: Tailwind CSS, no CSS modules
- Components live in src/components, one file per component
- Prefer function components with named exports
- Do not add new npm dependencies without asking first

This single file removes an enormous amount of ambiguity from every future prompt. Instead of re-explaining your stack in every message, Codex already knows it.

Scaffolding a New Prototype From a Blank Directory

The fastest way to see Codex's value is to start a prototype from nothing. Say you're mocking up a course-progress dashboard for a learning platform — a plausible ask if you're building something like an internal admin view for a cohort-based course.

mkdir course-dashboard-prototype && cd course-dashboard-prototype
npm create vite@latest . -- --template react-ts
npm install
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
codex

Inside the Codex session, a prompt like this gets you a real first draft rather than a vague stub:

Build a CourseDashboard component that shows:
- a header with the course title and a progress percentage badge
- a grid of module cards, each showing module name, lesson count,
  and a completion progress bar
- a sidebar listing "Up next" lessons with estimated time
Use Tailwind for styling, mock the data with a local array of
8 modules, and wire it into App.tsx so it renders on load.

Codex will typically respond by planning out the files it intends to touch (src/components/CourseDashboard.tsx, src/types/course.ts, src/App.tsx), then write them, then attempt to run the dev server or at least tsc --noEmit to confirm nothing is broken. This last part is the underrated advantage of an agentic CLI tool over a plain chat interface: it doesn't just generate code, it verifies the code compiles before handing it back to you.

A minimal version of what comes out might look like this:

type Module = {
  id: string;
  title: string;
  lessonCount: number;
  completedLessons: number;
};

const modules: Module[] = [
  { id: "m1", title: "Prompting Fundamentals", lessonCount: 6, completedLessons: 6 },
  { id: "m2", title: "Agentic Workflows", lessonCount: 8, completedLessons: 3 },
  { id: "m3", title: "Tool Use & MCP", lessonCount: 5, completedLessons: 0 },
];

function ModuleCard({ mod }: { mod: Module }) {
  const pct = Math.round((mod.completedLessons / mod.lessonCount) * 100);
  return (
    <div className="rounded-xl border border-gray-200 p-4 shadow-sm">
      <h3 className="font-semibold text-gray-900">{mod.title}</h3>
      <p className="text-sm text-gray-500">{mod.lessonCount} lessons</p>
      <div className="mt-3 h-2 w-full rounded-full bg-gray-100">
        <div
          className="h-2 rounded-full bg-indigo-500"
          style={{ width: `${pct}%` }}
        />
      </div>
      <span className="mt-1 block text-xs text-gray-400">{pct}% complete</span>
    </div>
  );
}

export default function CourseDashboard() {
  return (
    <div className="grid grid-cols-1 gap-4 p-6 sm:grid-cols-2 lg:grid-cols-3">
      {modules.map((m) => (
        <ModuleCard key={m.id} mod={m} />
      ))}
    </div>
  );
}

Nothing here is exotic, and that's the point. A prototype doesn't need clever abstractions — it needs to visually exist so a human can react to it. Codex is good at producing exactly this kind of "boring but correct" scaffold quickly, which frees you to spend your actual attention on the interaction design decisions that matter.

Iterating on Layout and Interaction With Natural Language

The real prototyping loop is the iteration, not the first scaffold. This is where Codex CLI's session model shines, because it retains context across turns — you don't have to re-paste the component every time you want a tweak.

A realistic iteration sequence looks like this:

Make the module cards clickable — clicking one should expand
it inline to show a list of individual lesson names with
checkmarks for completed ones.
The "Up next" sidebar should only show lessons from modules
that are in progress, sorted by estimated time ascending.
Add a subtle skeleton loading state for the module grid that
shows for 600ms before the real data renders, to simulate
an API call.

Each of these prompts is small and directional, which is intentional. Vague, large prompts ("make this dashboard better") produce vague, unpredictable diffs. Small, specific prompts produce small, reviewable diffs — and reviewability matters even in prototyping, because a broken build kills your momentum faster than anything else.

When a change doesn't look right after Codex applies it, the fastest recovery is not to explain the problem in prose — it's to paste the actual error or describe the visual discrepancy precisely:

The progress bar overflows its container on mobile widths.
Check the ModuleCard component — the width calculation is
probably not accounting for the parent's padding.

Codex CLI can also be pointed at a running dev server's console output if you're running commands through it, which turns debugging into a tight loop: it makes a change, runs npm run build or tsc, reads the error, and self-corrects before showing you anything. This is meaningfully different from copy-pasting into a chat window — the agent is closing its own feedback loop.

Working From a Design Reference Instead of a Blank Prompt

Prototyping rarely starts from nothing — usually you have a Figma screenshot, a competitor's UI, or a rough sketch someone drew in a meeting. Codex CLI supports image input, so you can hand it a screenshot directly and ask for a matching implementation.

codex "Recreate this pricing page layout in the attached screenshot
using our existing Tailwind config. Three-column pricing cards,
middle one highlighted as 'Most Popular'. Match spacing and
typography as closely as you can, use placeholder copy where
the text is unclear." --image ./pricing-mock.png

This is one of the more genuinely useful applications for prototyping specifically, because translating a static visual into working markup and CSS is exactly the kind of mechanical-but-tedious task that used to eat an afternoon. Codex won't get pixel-perfect spacing on the first try, but it gets close enough that the remaining adjustments are minutes, not hours.

A pattern worth adopting: after the first pass, ask Codex to self-critique against the reference image rather than just asking for "more accuracy," which is too vague to act on.

Compare your output against the screenshot again. List three
specific differences in spacing, font weight, or color, then
fix them.

Forcing an explicit comparison step tends to produce much better second-pass corrections than an open-ended "fix it" request, because it forces the model to actually look for discrete, addressable gaps instead of making a diffuse pass over the whole file.

Multi-Screen Prototypes and State That Needs to Feel Real

Single components are the easy case. Real prototypes usually need to demonstrate a flow — a signup form leading to an onboarding wizard leading to a dashboard — and stakeholders judge the flow, not the component. Codex handles multi-file, multi-route work fine, but you get much better results if you tell it about the flow up front rather than building screens independently and hoping they connect.

This prototype needs three routes using react-router:
/signup, /onboarding, /dashboard.
Signup collects name + email, stores it in a React context
(no backend), onboarding asks three preference questions using
that context, and dashboard greets the user by name from context
and shows the CourseDashboard component we already built.
Wire up the routing in App.tsx.

Notice the explicit "no backend" instruction. This is one of the most important habits for prototyping with any AI coding tool: be explicit about what should be faked. Left unconstrained, coding agents will sometimes reach for a real API call, a fetch to a nonexistent endpoint, or a database schema, because that's what "production-quality code" usually looks like in their training distribution. For a prototype, you want the opposite bias — mock data, in-memory state, setTimeout instead of real async delays — and you have to say so.

A useful context object for this kind of flow:

import { createContext, useContext, useState, type ReactNode } from "react";

type OnboardingData = {
  name: string;
  email: string;
  preferences: string[];
};

const OnboardingContext = createContext<{
  data: OnboardingData;
  setData: (d: Partial<OnboardingData>) => void;
} | null>(null);

export function OnboardingProvider({ children }: { children: ReactNode }) {
  const [data, setDataState] = useState<OnboardingData>({
    name: "",
    email: "",
    preferences: [],
  });

  const setData = (patch: Partial<OnboardingData>) =>
    setDataState((prev) => ({ ...prev, ...patch }));

  return (
    <OnboardingContext.Provider value={{ data, setData }}>
      {children}
    </OnboardingContext.Provider>
  );
}

export function useOnboarding() {
  const ctx = useContext(OnboardingContext);
  if (!ctx) throw new Error("useOnboarding must be used within OnboardingProvider");
  return ctx;
}

This is the kind of scaffolding that Codex generates reliably and that you rarely need to hand-correct — context providers, custom hooks, and route wiring are well-trodden patterns with low ambiguity, which is exactly where an agent's output is most trustworthy.

Where Codex Struggles and How to Compensate

It's worth being honest about the failure modes, because trusting a tool blindly is how prototypes quietly become broken production code.

Visual taste is inconsistent. Codex can implement a spec precisely, but if you don't specify spacing, hierarchy, or color intent, it will default to reasonable-but-generic choices — a lot of rounded-lg, shadow-sm, and safe grays. If your prototype needs to feel distinctive rather than merely functional, you still need to make the aesthetic calls yourself and describe them precisely, or start from a real design reference as shown above.

It can lose track of a large codebase's conventions over long sessions. In a long prototyping session with many back-and-forth turns, Codex occasionally reverts to a default pattern (say, CSS-in-JS) even after you've established Tailwind as the convention. When you notice this, the fix isn't to get frustrated — it's to restate the constraint plainly and, if it keeps happening, add it explicitly to AGENTS.md so it's structurally reinforced rather than conversationally repeated.

It will "fix" things you didn't ask about. Occasionally, in the course of implementing a request, Codex touches an unrelated file — reformatting an import, renaming a variable, adjusting an unrelated component's styling. For prototyping this is usually harmless, but it's still worth reviewing diffs rather than blindly accepting, because an unrequested change can introduce a regression in a screen you already approved.

It cannot judge product fit. Codex can build exactly what you describe, but it has no opinion on whether the flow you're building actually solves the user's problem. That judgment call is still entirely yours — the tool accelerates execution, not product thinking.

The practical mitigation for all of the above is the same: keep iterations small, review diffs even in suggest mode, and treat Codex's output as a strong first draft rather than a final answer. This is not a limitation unique to Codex — it's true of every current-generation coding agent, and treating it as such will save you from surprises.

Turning a Prototype Into Something Real

At some point a prototype either dies (you learned what you needed and move on) or gets promoted toward production. When promotion happens, resist the urge to keep vibing with the same loose prompting style that got you the fast draft — the bar for correctness changes, and your prompting should change with it.

A useful transition checklist to run through with Codex once a prototype is validated:

  • Replace all mock data and in-memory context with real API calls and proper loading/error states
  • Add prop types and runtime validation at data boundaries (Zod or similar) rather than trusting shapes
  • Extract any inline styling decisions into a real design system or shared component library, not one-off Tailwind classes copy-pasted per screen
  • Add basic accessibility passes — label associations, focus management, keyboard navigation on interactive cards
  • Write at least smoke-level tests for the flows a stakeholder actually clicked through and approved

You can ask Codex to help with each of these steps individually, but treat them as separate, reviewed tasks rather than one giant "make this production ready" prompt. The granularity that made prototyping fast is the same granularity that makes hardening safe — big vague asks produce big vague diffs at every stage of a project, not just the early one.

A Realistic Weekly Workflow

Putting this together, a workflow that holds up in practice for a frontend engineer doing regular prototyping work looks roughly like this:

  1. Start each new prototype in its own directory with a minimal AGENTS.md describing stack and conventions
  2. Scaffold the first screen with a specific, detailed prompt rather than a one-liner
  3. Iterate in small, single-purpose prompts, reviewing each diff before accepting
  4. When working from a design reference, use image input and force an explicit self-comparison pass
  5. Be explicit about what should be mocked versus real, every time — never assume the default
  6. When a prototype is approved, run a deliberate hardening pass instead of shipping the fast draft as-is
  7. Delete the ones that didn't work without guilt — a fast, cheap failed prototype is the entire point

None of this requires deep AI expertise. It requires the same discipline you'd apply to working with a very fast, very literal junior engineer: give clear instructions, review the work, and don't skip the parts that matter just because the first draft arrived quickly.

Where to Go Deeper

Getting genuinely fast with Codex CLI — beyond the basics covered here — means understanding its approval modes in depth, how it plans multi-file changes, how to structure AGENTS.md files for larger codebases, and how to combine it with a real dev server loop for continuous visual feedback instead of static file generation. If you want a structured, hands-on path through all of that rather than piecing it together from documentation, teachyou.ai's OpenAI Codex CLI Tutorial course walks through exactly this: setting up Codex CLI properly, real prototyping and engineering workflows, and the judgment calls that separate a fast first draft from code you can actually ship.