app-store-screenshots-generator

Generate production-ready App Store screenshots for iOS apps using AI agents, Next.js, and html-to-image

3,807 stars

Best use case

app-store-screenshots-generator is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generate production-ready App Store screenshots for iOS apps using AI agents, Next.js, and html-to-image

Teams using app-store-screenshots-generator should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/app-store-screenshots-generator/SKILL.md --create-dirs "https://raw.githubusercontent.com/openclaw/skills/main/skills/adisinghstudent/app-store-screenshots-generator/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/app-store-screenshots-generator/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How app-store-screenshots-generator Compares

Feature / Agentapp-store-screenshots-generatorStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generate production-ready App Store screenshots for iOS apps using AI agents, Next.js, and html-to-image

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# App Store Screenshots Generator

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

A skill for AI coding agents (Claude Code, Cursor, Windsurf, Codex, etc.) that generates production-ready App Store screenshots for iOS apps. It scaffolds a Next.js project, designs advertisement-style slides, and exports PNGs at all required Apple resolutions.

## What This Skill Does

- Asks about your app's brand, features, and style preferences before building anything
- Scaffolds a minimal Next.js + TypeScript + Tailwind project
- Designs each screenshot as an **advertisement** (not a UI showcase)
- Writes compelling copy using proven App Store copywriting patterns
- Renders screenshots with a built-in iPhone mockup frame
- Exports PNGs at all 4 Apple-required sizes (6.9", 6.5", 6.3", 6.1")
- Supports multi-locale screenshot sets, RTL-aware layouts, and reusable theme presets

## Install

### Using npx skills (recommended)

```bash
# Install for current project
npx skills add ParthJadhav/app-store-screenshots

# Install globally (available across all projects)
npx skills add ParthJadhav/app-store-screenshots -g

# Install for a specific agent
npx skills add ParthJadhav/app-store-screenshots -a claude-code
```

### Manual install

```bash
git clone https://github.com/ParthJadhav/app-store-screenshots ~/.claude/skills/app-store-screenshots
```

## Usage

Once installed, just describe what you need:

```
> Build App Store screenshots for my habit tracker.
  The app helps people stay consistent with simple daily routines.
  I want 6 slides, clean/minimal style, warm neutrals, and a calm premium feel.
```

The agent will ask clarifying questions about brand colors, fonts, features, style direction, number of slides, and locales before generating anything.

## Project Structure

The skill scaffolds this layout:

```
project/
├── public/
│   ├── mockup.png              # iPhone frame with transparent screen area
│   ├── app-icon.png            # Your app icon
│   ├── screenshots/            # App screenshots (optionally nested by locale)
│   │   ├── en/
│   │   ├── de/
│   │   └── ar/
│   └── screenshots-ipad/       # Optional iPad screenshots
├── src/app/
│   ├── layout.tsx              # Font setup
│   └── page.tsx                # Entire screenshot generator (single file)
├── package.json
└── next.config.ts
```

**The entire generator lives in a single `page.tsx` file.** Run the dev server, open the browser, and click any screenshot to export it as a PNG.

## Core page.tsx Pattern

```tsx
// src/app/page.tsx — minimal scaffold pattern
"use client";

import { toPng } from "html-to-image";
import { useRef, useState } from "react";

// --- Design tokens / theme presets ---
const THEMES = {
  "clean-light": {
    bg: "#F6F1EA",
    fg: "#171717",
    accent: "#5B7CFA",
    font: "Inter",
  },
  "dark-bold": {
    bg: "#0B1020",
    fg: "#F8FAFC",
    accent: "#8B5CF6",
    font: "Inter",
  },
  "warm-editorial": {
    bg: "#F7E8DA",
    fg: "#2B1D17",
    accent: "#D97706",
    font: "Playfair Display",
  },
} as const;

type ThemeKey = keyof typeof THEMES;

// --- Export sizes (width x height in px) ---
const EXPORT_SIZES = {
  "6.9": { w: 1320, h: 2868 },
  "6.5": { w: 1284, h: 2778 },
  "6.3": { w: 1206, h: 2622 },
  "6.1": { w: 1125, h: 2436 },
} as const;

// --- Slide data ---
const SLIDES = [
  {
    id: 1,
    headline: "Track Every Habit,\nEvery Day",
    subheadline: "Build streaks that stick",
    screenshot: "/screenshots/en/home.png",
    layout: "phone-right", // varies per slide: phone-left | phone-right | phone-center
  },
  {
    id: 2,
    headline: "See Your Progress\nAt a Glance",
    subheadline: "Beautiful weekly summaries",
    screenshot: "/screenshots/en/stats.png",
    layout: "phone-left",
  },
  // ... more slides
];

// --- Screenshot canvas (designed at 6.9" — 1320x2868) ---
function ScreenshotSlide({
  slide,
  theme,
  slideRef,
}: {
  slide: (typeof SLIDES)[0];
  theme: (typeof THEMES)[ThemeKey];
  slideRef: React.RefObject<HTMLDivElement>;
}) {
  return (
    <div
      ref={slideRef}
      style={{
        width: 1320,
        height: 2868,
        background: theme.bg,
        color: theme.fg,
        fontFamily: theme.font,
        position: "relative",
        overflow: "hidden",
      }}
    >
      {/* Headline */}
      <div
        style={{
          position: "absolute",
          top: 180,
          left: 80,
          right: 80,
          fontSize: 96,
          fontWeight: 800,
          lineHeight: 1.1,
          whiteSpace: "pre-line",
        }}
      >
        {slide.headline}
      </div>

      {/* Subheadline */}
      <div
        style={{
          position: "absolute",
          top: 520,
          left: 80,
          fontSize: 48,
          color: theme.accent,
          fontWeight: 500,
        }}
      >
        {slide.subheadline}
      </div>

      {/* iPhone mockup + app screenshot */}
      <div
        style={{
          position: "absolute",
          bottom: 0,
          right: slide.layout === "phone-right" ? 0 : "auto",
          left: slide.layout === "phone-left" ? 0 : "auto",
        }}
      >
        {/* App screenshot inside mockup */}
        <div style={{ position: "relative", width: 660, height: 1430 }}>
          <img
            src={slide.screenshot}
            style={{
              position: "absolute",
              top: 28,
              left: 24,
              width: 612,
              borderRadius: 52,
            }}
            alt=""
          />
          {/* Mockup frame sits on top */}
          <img
            src="/mockup.png"
            style={{ position: "absolute", inset: 0, width: "100%" }}
            alt=""
          />
        </div>
      </div>
    </div>
  );
}

// --- Export helper ---
async function exportSlide(
  ref: React.RefObject<HTMLDivElement>,
  slideId: number,
  size: keyof typeof EXPORT_SIZES,
  locale: string,
  themeName: string
) {
  if (!ref.current) return;

  const { w, h } = EXPORT_SIZES[size];
  const scale = w / 1320; // designed at 1320px wide

  const dataUrl = await toPng(ref.current, {
    width: w,
    height: h,
    style: { transform: `scale(${scale})`, transformOrigin: "top left" },
  });

  const link = document.createElement("a");
  link.download = `slide-${slideId}-${locale}-${themeName}-${size}in.png`;
  link.href = dataUrl;
  link.click();
}

// --- Main page ---
export default function ScreenshotGenerator() {
  const [activeTheme, setActiveTheme] = useState<ThemeKey>("clean-light");
  const [activeLocale, setActiveLocale] = useState("en");
  const slideRefs = useRef<React.RefObject<HTMLDivElement>[]>(
    SLIDES.map(() => ({ current: null } as React.RefObject<HTMLDivElement>))
  );

  const theme = THEMES[activeTheme];

  return (
    <div style={{ padding: 40, background: "#111", minHeight: "100vh" }}>
      {/* Controls */}
      <div style={{ display: "flex", gap: 16, marginBottom: 40 }}>
        {(Object.keys(THEMES) as ThemeKey[]).map((t) => (
          <button
            key={t}
            onClick={() => setActiveTheme(t)}
            style={{
              padding: "8px 16px",
              background: activeTheme === t ? "#fff" : "#333",
              color: activeTheme === t ? "#000" : "#fff",
              border: "none",
              borderRadius: 8,
              cursor: "pointer",
            }}
          >
            {t}
          </button>
        ))}

        {/* Bulk export all slides at all sizes */}
        <button
          onClick={async () => {
            for (const [i, slide] of SLIDES.entries()) {
              for (const size of Object.keys(EXPORT_SIZES) as (keyof typeof EXPORT_SIZES)[]) {
                await exportSlide(
                  slideRefs.current[i],
                  slide.id,
                  size,
                  activeLocale,
                  activeTheme
                );
              }
            }
          }}
          style={{
            padding: "8px 16px",
            background: theme.accent,
            color: "#fff",
            border: "none",
            borderRadius: 8,
            cursor: "pointer",
            marginLeft: "auto",
          }}
        >
          Export All
        </button>
      </div>

      {/* Slides */}
      <div style={{ display: "flex", flexDirection: "column", gap: 40 }}>
        {SLIDES.map((slide, i) => (
          <div key={slide.id}>
            <ScreenshotSlide
              slide={slide}
              theme={theme}
              slideRef={slideRefs.current[i]}
            />
            {/* Per-slide export buttons */}
            <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
              {(Object.keys(EXPORT_SIZES) as (keyof typeof EXPORT_SIZES)[]).map(
                (size) => (
                  <button
                    key={size}
                    onClick={() =>
                      exportSlide(
                        slideRefs.current[i],
                        slide.id,
                        size,
                        activeLocale,
                        activeTheme
                      )
                    }
                    style={{
                      padding: "6px 12px",
                      background: "#222",
                      color: "#fff",
                      border: "1px solid #444",
                      borderRadius: 6,
                      cursor: "pointer",
                      fontSize: 13,
                    }}
                  >
                    Export {size}"
                  </button>
                )
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}
```

## Multi-Locale Setup

Organize screenshots under locale folders and swap the base path:

```
public/screenshots/
├── en/home.png
├── de/home.png
└── ar/home.png   ← RTL locale
```

```tsx
// Locale-aware copy dictionary
const COPY: Record<string, Record<number, { headline: string; subheadline: string }>> = {
  en: {
    1: { headline: "Track Every Habit,\nEvery Day", subheadline: "Build streaks that stick" },
    2: { headline: "See Your Progress\nAt a Glance", subheadline: "Beautiful weekly summaries" },
  },
  de: {
    1: { headline: "Jede Gewohnheit\nIm Blick", subheadline: "Baue Serien auf, die halten" },
    2: { headline: "Fortschritt auf\neinen Blick", subheadline: "Wöchentliche Übersichten" },
  },
  ar: {
    1: { headline: "تتبع كل عادة\nكل يوم", subheadline: "ابنِ سلاسل تدوم" },
    2: { headline: "اطّلع على تقدمك\nدفعة واحدة", subheadline: "ملخصات أسبوعية جميلة" },
  },
};

// RTL-aware canvas wrapper
function LocaleCanvas({ locale, children }: { locale: string; children: React.ReactNode }) {
  const isRTL = ["ar", "he", "fa"].includes(locale);
  return (
    <div dir={isRTL ? "rtl" : "ltr"} style={{ textAlign: isRTL ? "right" : "left" }}>
      {children}
    </div>
  );
}
```

## Dependencies (package.json)

```json
{
  "dependencies": {
    "next": "^14.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "html-to-image": "^1.11.11"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "@types/react": "^18.0.0",
    "@types/node": "^20.0.0",
    "tailwindcss": "^3.4.0",
    "autoprefixer": "^10.4.0",
    "postcss": "^8.4.0"
  }
}
```

## Starting the Dev Server

The skill auto-detects your package manager (bun preferred):

```bash
# bun (preferred)
bun install && bun dev

# pnpm
pnpm install && pnpm dev

# yarn
yarn && yarn dev

# npm
npm install && npm run dev
```

Then open [http://localhost:3000](http://localhost:3000) to view the screenshot generator.

## Export Sizes Reference

| Display | Width | Height | Notes |
|---------|-------|--------|-------|
| 6.9" | 1320px | 2868px | Design base size |
| 6.5" | 1284px | 2778px | Scale: 0.973 |
| 6.3" | 1206px | 2622px | Scale: 0.914 |
| 6.1" | 1125px | 2436px | Scale: 0.852 |

> **Note:** Use a 6.1" simulator to capture your initial app screenshots — this avoids resolution adjustments later.

## Design Principles the Agent Follows

1. **Screenshots are ads, not docs** — each slide sells one idea
2. **One-second rule** — headline must be readable at App Store thumbnail size
3. **Vary layouts** — no two adjacent slides share the same phone placement (`phone-left` / `phone-right` / `phone-center`)
4. **Style is user-driven** — no hardcoded colors; everything flows from theme tokens
5. **First slide = strongest benefit** — not the most complex feature

## Quality Checklist

Before exporting, verify each slide:

- [ ] Headline communicates exactly one idea in ~1 second
- [ ] First slide leads with the strongest user benefit (not a feature)
- [ ] Adjacent slides use different phone placement layouts
- [ ] Decorative elements support the message, not obscure the UI
- [ ] Text and framing look correct after scaling to smallest export size (6.1")
- [ ] RTL locales have `dir="rtl"` set and layouts feel native (not mechanically mirrored)

## Example Prompts

```text
# Habit tracker
Build App Store screenshots for my habit tracker.
The app helps people stay consistent with simple daily routines.
I want 6 slides, clean/minimal style, warm neutrals, and a calm premium feel.

# Finance app
Generate App Store screenshots for my personal finance app.
Main strengths: fast expense capture, clear monthly trends, shared budgets.
Sharp modern style, high contrast, 7 slides.

# Multi-locale + themes
Build App Store screenshots for my language learning app.
I need English, German, and Arabic screenshot sets.
Use two themes: clean-light and dark-bold.
Make Arabic slides feel RTL-native, not just translated.
```

## Troubleshooting

**html-to-image exports blank or white images**
- Ensure all images are served from `/public` (same origin). Cross-origin images block canvas export.
- Add `crossOrigin="anonymous"` to `<img>` tags if loading from a CDN.
- Check the browser console for CORS errors.

**Mockup frame not aligning with screenshot**
- The included `mockup.png` has pre-measured transparent screen area. Use `position: absolute` with the app screenshot behind the frame, not inside it.
- Use `top: 28px, left: 24px` as the starting offset for a 660px-wide mockup.

**Export scaling looks blurry**
- Always design at 1320×2868 (6.9" base) and scale down — never design small and scale up.
- Pass explicit `width` and `height` to `toPng()` with the correct scale transform.

**Font not loading in exports**
- Load fonts via `next/font` or a `<style>` tag inside the canvas div, not just in `layout.tsx`.
- Call `document.fonts.ready` before triggering `toPng()`.

```tsx
// Wait for fonts before export
await document.fonts.ready;
const dataUrl = await toPng(ref.current, { width: w, height: h });
```

**Simulator screenshots wrong resolution**
- Always capture from the **6.1" simulator** as the starting point to minimize later adjustments.

## Requirements

- Node.js 18+
- One of: bun, pnpm, yarn, or npm
- Claude Code, Cursor, Windsurf, Codex, or any agent that reads skill files

Related Skills

Invoice Generator

3807
from openclaw/skills

Creates professional invoices in markdown and HTML

Workflow & Productivity

Incident Postmortem Generator

3807
from openclaw/skills

Generate blameless incident postmortems from raw notes, Slack threads, or bullet points.

DevOps & Infrastructure

Partnership Agreement Generator

3807
from openclaw/skills

Generate comprehensive partnership agreements, joint venture frameworks, and strategic alliance documents for B2B relationships.

Legal Documents & Agreements

Investor Update Generator

3807
from openclaw/skills

Generate professional monthly/quarterly investor updates that keep stakeholders informed and build trust.

Workflow & Productivity

Employee Onboarding Generator

3807
from openclaw/skills

Build a structured 90-day onboarding plan for any role. Covers pre-boarding, Day 1, Week 1, 30/60/90-day milestones, buddy assignments, and success metrics.

Workflow & Productivity

Employee Handbook Generator

3807
from openclaw/skills

Build a complete, customized employee handbook for your company. Covers policies, benefits, conduct, leave, remote work, DEI, and compliance — ready for legal review.

Content & Documentation

IT Disaster Recovery Plan Generator

3807
from openclaw/skills

Build production-ready disaster recovery plans that actually get followed when things break.

DevOps & Infrastructure

Compliance Audit Generator

3807
from openclaw/skills

Run internal compliance audits against major frameworks without hiring a consultant.

Security

API Documentation Generator

3807
from openclaw/skills

Generate production-ready API documentation from endpoint descriptions. Outputs OpenAPI 3.0, markdown reference docs, and SDK quickstart guides.

Coding & Development

Annual Report Generator

3807
from openclaw/skills

Build a complete annual business report from raw data. Covers financial performance, operational metrics, strategic highlights, and forward-looking guidance.

Workflow & Productivity

daily-report-generator

3807
from openclaw/skills

Automatically generate daily/weekly work reports from git commits, calendar events, and task lists. Use when you need to quickly create professional work reports without manual effort.

Workflow & Productivity

hr-policy-generator

3807
from openclaw/skills

Comprehensive HR policy development covering attendance, time-off, overtime, remote work, and compliance. Generates structured policy documents, legal checklists, exception handling frameworks, and employee communication plans tailored to company size, work arrangement, and jurisdiction.

Workflow & Productivity