AINode.jsAutomation

Screenshot to Code with AI

TT
TopicTrick Team
Screenshot to Code with AI

Screenshot to Code with AI

Translate any UI screenshot or Figma export into working code with a single API call. This tool uses GPT-4o Vision to analyse the visual layout and generate HTML/CSS, React + Tailwind, or Vue.js code that matches the design.

This is Tool 20 of the Build 50 AI Automation Tools course.


What You'll Build

  • POST /convert — upload a screenshot, specify framework, receive working code
  • Supports HTML+CSS, React+Tailwind, React+styles, and Vue
  • POST /convert/refine — send the generated code back with feedback to refine it

Setup

bash
mkdir screenshot-to-code && cd screenshot-to-code
npm init -y
npm install express multer openai dotenv
bash
# .env
OPENAI_API_KEY=sk-your-key-here
PORT=3000

Code Generation Service

js
// src/services/codegenService.js
import OpenAI from 'openai';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const FRAMEWORK_PROMPTS = {
  'html': `Generate clean, semantic HTML5 with embedded CSS.
Rules: Use CSS custom properties for colors, flexbox/grid for layout, responsive at 320px and 768px breakpoints.
Return JSON: {"html": "complete HTML document", "cssNotes": "any notable CSS decisions"}`,

  'react-tailwind': `Generate a React functional component using Tailwind CSS utility classes only.
Rules: Use React hooks for state, Tailwind v3 classes only (no custom CSS), responsive classes (sm: md: lg:), accessible HTML semantics.
Return JSON: {"componentName": "string", "code": "complete React component with import statements", "dependencies": ["any npm packages needed beyond react"]}`,

  'react-styles': `Generate a React functional component using inline styles and CSS-in-JS patterns.
Rules: All styles as JavaScript objects, use React.useState for interactive elements.
Return JSON: {"componentName": "string", "code": "complete React component"}`,

  'vue': `Generate a Vue 3 Single File Component using the Composition API and scoped styles.
Rules: Use <script setup> syntax, scoped CSS, reactive() or ref() for state.
Return JSON: {"componentName": "string", "code": "complete .vue SFC file"}`,
};

export async function screenshotToCode(buffer, mimetype, framework = 'react-tailwind', additionalInstructions = '') {
  const dataUrl = `data:${mimetype};base64,${buffer.toString('base64')}`;
  const frameworkPrompt = FRAMEWORK_PROMPTS[framework] || FRAMEWORK_PROMPTS['react-tailwind'];

  const instructions = additionalInstructions
    ? `Additional instructions: ${additionalInstructions}`
    : '';

  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `You are an expert frontend developer. Convert the UI screenshot into production-ready code.
${frameworkPrompt}
${instructions}
Analyse the screenshot carefully: identify the layout structure, components, spacing, colors, typography, and interactive elements.
Generate complete, working code that visually matches the screenshot as closely as possible.`,
      },
      {
        role: 'user',
        content: [
          { type: 'text', text: 'Convert this UI screenshot to code:' },
          { type: 'image_url', image_url: { url: dataUrl, detail: 'high' } },
        ],
      },
    ],
    temperature: 0.2,
    max_tokens: 4000,
    response_format: { type: 'json_object' },
  });

  return JSON.parse(response.choices[0].message.content);
}

export async function refineCode(existingCode, feedback, framework = 'react-tailwind') {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `You are an expert frontend developer. Refine the ${framework} code based on the feedback.
Return JSON with the same structure as the original code, with changes applied.`,
      },
      {
        role: 'user',
        content: `EXISTING CODE:\n${existingCode}\n\nFEEDBACK TO APPLY:\n${feedback}`,
      },
    ],
    temperature: 0.3,
    max_tokens: 4000,
    response_format: { type: 'json_object' },
  });

  return JSON.parse(response.choices[0].message.content);
}

API Routes + Server

js
// src/server.js
import 'dotenv/config';
import express from 'express';
import multer from 'multer';
import { screenshotToCode, refineCode } from './services/codegenService.js';

const app = express();
app.use(express.json({ limit: '5mb' }));
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 10 * 1024 * 1024 },
  fileFilter: (_req, file, cb) =>
    ['image/jpeg', 'image/png', 'image/webp'].includes(file.mimetype)
      ? cb(null, true) : cb(new Error('PNG, JPEG, or WebP required')),
});

app.post('/convert', upload.single('screenshot'), async (req, res, next) => {
  try {
    if (!req.file) return res.status(400).json({ error: 'No screenshot uploaded' });
    const { framework, instructions } = req.body;
    const result = await screenshotToCode(req.file.buffer, req.file.mimetype, framework, instructions);
    res.json({ success: true, framework: framework || 'react-tailwind', ...result });
  } catch (err) { next(err); }
});

app.post('/convert/refine', async (req, res, next) => {
  try {
    const { code, feedback, framework } = req.body;
    if (!code || !feedback) return res.status(400).json({ error: 'code and feedback required' });
    const result = await refineCode(code, feedback, framework);
    res.json({ success: true, ...result });
  } catch (err) { next(err); }
});

app.use((err, _req, res, _next) => res.status(500).json({ error: err.message }));
app.listen(process.env.PORT ?? 3000, () => console.log('Screenshot to Code running'));

Testing

bash
# Convert a screenshot to React + Tailwind
curl -X POST http://localhost:3000/convert \
  -F "screenshot=@ui-design.png" \
  -F "framework=react-tailwind" \
  -F "instructions=Use a dark theme. The primary color is indigo-600."

# Refine the generated code
curl -X POST http://localhost:3000/convert/refine \
  -H "Content-Type: application/json" \
  -d '{
    "code": "const Card = () => ...",
    "feedback": "Make the card shadow softer and increase the padding",
    "framework": "react-tailwind"
  }'

Sample response:

json
{
  "componentName": "HeroSection",
  "code": "import { useState } from 'react';\n\nconst HeroSection = () => {\n  return (\n    <div className=\"min-h-screen bg-gradient-to-br from-indigo-50 to-white flex items-center justify-center px-4\">\n      <div className=\"max-w-4xl mx-auto text-center\">\n        <span className=\"inline-block px-4 py-2 bg-indigo-100 text-indigo-700 rounded-full text-sm font-semibold mb-6\">\n          New: AI-powered workflows\n        </span>\n        <h1 className=\"text-5xl font-bold text-gray-900 mb-6 leading-tight\">\n          Build faster with <span className=\"text-indigo-600\">AI automation</span>\n        </h1>\n        <p className=\"text-xl text-gray-600 mb-10 max-w-2xl mx-auto\">\n          Automate repetitive tasks and ship 10x faster.\n        </p>\n        <div className=\"flex gap-4 justify-center\">\n          <button className=\"bg-indigo-600 hover:bg-indigo-700 text-white px-8 py-4 rounded-xl font-semibold transition-colors\">\n            Get Started Free\n          </button>\n          <button className=\"border-2 border-gray-200 hover:border-indigo-600 text-gray-700 px-8 py-4 rounded-xl font-semibold transition-colors\">\n            See Demo\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default HeroSection;",
  "dependencies": []
}

Batch Figma Export Processing

bash
# Process a folder of Figma exports
for f in figma-exports/*.png; do
  curl -X POST http://localhost:3000/convert \
    -F "screenshot=@$f" \
    -F "framework=react-tailwind" \
    -o "components/$(basename $f .png).json"
done

Build 50 AI Automation Tools — Tool 20 of 50

Screenshot-to-code is live. Continue to Tool 21 to build an ID card OCR and validation tool.


    Summary

    • detail: 'high' is essential for screenshot-to-code — low detail mode misses UI specifics
    • Framework-specific prompts produce code conventions appropriate to each framework
    • Refine endpoint implements a human-in-the-loop workflow — generate, review, refine iteratively
    • The batch Figma export script bridges design handoff without per-component manual effort
    • For pixel-perfect output, combine AI generation with a CSS-in-JS library like Styled Components

    Continue to Tool 21: ID Card & Document OCR with AI Validation →