react-shot
v0.1.0
Published
App Store screenshot generator. Write designs in React, export production-ready PNGs for every locale and device.
Downloads
80
Maintainers
Readme
react-shot
react-shot is a Bun-first screenshot SDK for App Store and Play Store creative. You write screenshot sets in React, preview them locally, and export production-ready PNG slices with Puppeteer and Sharp.
The current SDK is built around one idea:
Text is the argument. UI is the proof.
Instead of shipping generic “5 screens with different colors”, react-shot now gives you copy-first building blocks and creative families for agency-style screenshot sets.
Requirements
- Runtime: Bun
- Install in projects:
bun add react-shot - One-off commands:
bunx react-shot ...
Install
bun add react-shotQuick Start
mkdir my-screenshots
cd my-screenshots
bunx react-shot init
bun install
# Add raw simulator screenshots
cp ~/simulator-screenshots/*.png public/assets/screenshots/ios/
# Rewrite the generated headlines first
$EDITOR compositions/hero/index.tsx
# Preview the set
bun run dev
# Export all locales and devices
bun run export -- hero --all --storeCLI
react-shot init
react-shot dev
react-shot export <id>
react-shot export <id> --locale tr
react-shot export <id> --device ipad
react-shot export <id> --all
react-shot export <id> --store
react-shot build
react-shot --versionStory Model
The default screenshot system is a 5-slot sequence:
- Hook
- Shift
- Proof
- Delivery
- Payoff
The first three slots should answer:
- Why should I care?
- What changes after I install?
- Why should I believe you?
If the copy reads like feature labels, rewrite it before touching the layout.
Creative Families
react-shot now ships stronger starter directions instead of a large pool of color-swapped demos:
editorial-travelcreator-boldtransit-utilityplayful-commercepremium-neutral
Each family is meant to behave like one campaign with a consistent:
- headline rhythm
- device placement
- background system
- proof treatment
- merchandising language
Config
import { defineConfig } from "react-shot/config";
export default defineConfig({
compositionsDir: "./compositions",
publicDir: "./public",
creativeFamily: "premium-neutral",
brandColor: "#1F2937",
targetDisplay: "iphone-67",
port: 5174,
export: {
outputDir: "./appstore",
deviceFolders: {
ios: "APP_IPHONE_67",
ipad: "APP_IPAD_PRO_6GEN_129",
},
},
});Additional top-level fields:
creativeFamilybrandColortargetDisplay
Unknown top-level config keys now warn to catch typos early.
Writing Compositions
The preferred workflow is:
- Write the 5 headlines.
- Map each headline to the strongest screen.
- Choose one creative family.
- Build the set with reusable blocks.
Example:
import {
Canvas,
Slice,
SolidBg,
HeroHeadlineBlock,
DeviceStage,
ScreenshotBreakout,
ProofStrip,
} from "react-shot";
import { appstoreIOS } from "react-shot/presets";
export default function Hero() {
return (
<Canvas preset={appstoreIOS}>
<Slice index={0} bg={<SolidBg color="#F3F4F6" />} align="center" justify="start" py={90}>
<div style={{ flex: 1, width: "100%", display: "flex", justifyContent: "center", alignItems: "center" }}>
<HeroHeadlineBlock
kicker="The hook"
headline={<>Stop wasting time<br />on busy work</>}
subtitle="Lead with the pain, not the feature list."
proofItems={["4.9 rating", "Loved by teams"]}
accentColor="#2563EB"
headlineColor="#111827"
subtitleColor="rgba(17,24,39,0.64)"
/>
</div>
<DeviceStage
variant="iphone-16-pro"
frameColor="white"
screenshot="/assets/screenshots/ios/1.png"
width={1000}
bleedBottom={120}
>
<ScreenshotBreakout
x={120}
y={980}
width={980}
bgColor="#FFFFFF"
textColor="#111827"
title="One message per screen wins"
>
<ProofStrip items={["Pain first", "Outcome next", "Proof third"]} />
</ScreenshotBreakout>
</DeviceStage>
</Slice>
</Canvas>
);
}Register compositions in compositions/index.ts with family and narrative metadata:
import { lazy } from "react";
import type { CompositionEntry } from "react-shot/types";
import { appstoreIOS, appstoreIPad } from "react-shot/presets";
export const compositions: CompositionEntry[] = [
{
id: "hero",
name: "Copy-First Hero",
component: lazy(() => import("./hero")),
preset: appstoreIOS,
ipadPreset: appstoreIPad,
locales: ["en", "tr"],
family: "premium-neutral",
narrative: {
name: "Copy-First Hero",
family: "premium-neutral",
brandColor: "#F3F4F6",
slots: [
{ role: "hook", headline: { verb: "STOP", outcome: "WASTING TIME", support: "Lead with the pain" } },
{ role: "shift", headline: { verb: "GET", outcome: "INSTANT CLARITY", support: "Show the shift" } },
{ role: "proof", headline: { verb: "TRUST", outcome: "A REAL RESULT", support: "Back up the claim" } },
{ role: "feature", headline: { verb: "USE", outcome: "THE CORE WORKFLOW", support: "Reveal the mechanic" } },
{ role: "cta", headline: { verb: "START", outcome: "TODAY", support: "Close with the payoff" } },
],
},
},
];New Building Blocks
High-level components for screenshot campaigns:
HeroHeadlineBlockProofStripStoryBadgeDeviceStageScreenshotBreakoutFloatingMerch
These sit on top of the lower-level canvas, slice, text, device, and decorative primitives.
Presets
appstoreIOS
appstoreIOS4
appstoreIOS6
appstoreIOS65
appstoreIOS69
appstoreIPad
playstore
socialInstagram
socialTwitterApp Store portrait presets now include crop-safe metadata for text and merchandising.
Types
import type {
CompositionEntry,
CompositionProps,
ScreenshotNarrative,
StorySlot,
StorySlotRole,
BenefitHeadline,
CreativeFamily,
CropSafeArea,
DeviceVariant,
} from "react-shot/types";Export Output
react-shot export <id> --store writes output organized by locale and device:
appstore/
en/
APP_IPHONE_67/01.png
APP_IPHONE_67/02.png
tr/
APP_IPAD_PRO_6GEN_129/01.pngHow It Works
- Compositions are React components rendered at exact store dimensions.
react-shot devstarts a Vite server with gallery and preview routes.react-shot exportlaunches Puppeteer, renders the full canvas, and slices it into final PNGs with Sharp.
License
MIT
