PictureCode — see what your code looks like
PictureCode takes pictures of your running web app and shows the exact component you are editing, pinned next to the minimap. Open search-results.tsx and you see the search results page. Open search-header.tsx and you see only the header.
- Automatic. Run
yarn dev, npm run dev, pnpm dev, next dev, ng serve or similar in the VS Code terminal. PictureCode spots the dev server URL in the terminal output and photographs every page in the background.
- Works without a backend. If an API call fails (server offline, 401, 500, timeout), PictureCode answers it with fake data built from your TypeScript types.
http.post<ITicketSearchResponse>('/tickets/search') gets back a believable ITicketSearchResponse: 6 tickets with names, dates, statuses from your union types, and a total that matches the list.
- One picture per page. Each page is rendered once. Component pictures are cut from that page image inside the browser and saved as WebP. Files are named by their content, so a component that didn't change costs nothing to save again, and pages that look the same are stored once.
- Incremental. When you save a file, only the pages that show components from that file are captured again.
Every component gets a picture
PictureCode reaches components in three ways, in this order:
- Pages. It visits every route it finds (router config, file-based routes, links), filling route parameters with fake values.
- UI states. On each page it clicks the buttons that open things: modals, drawers, dropdowns, tabs, accordions. Then it pictures the components that appear, such as a
TicketFilters modal and the FilterRange inputs inside it. While exploring:
- Requests that could change data (POST/PUT/PATCH/DELETE) are answered with fake data. They never reach a real backend.
- Links, form-submit buttons and destructive-looking buttons (Delete, Log out, Pay…) are never clicked.
- For anything that needs several steps, such as step 2 of a form, describe the steps in
interactions (see below).
- On its own. A component that no page shows, for example a shared input or a widget not used anywhere yet, is rendered alone:
- Your app's real providers are copied from your entry file (React Query, router, i18n, theme…).
- Props are generated from its TypeScript props type.
ReactNode becomes text and callbacks become no-ops.
- Its API calls are answered with data generated from your types.
- This happens when you open the file, and in the background after each full capture. It needs a Vite dev server, with React or Vue.
Where the pictures show up
| Where |
What |
| Next to the minimap |
A floating card at the top-right of the editor. It shows the component at your cursor, or the whole page when the file is a page. It follows you as you scroll. Toggle it with Ctrl/Cmd+Alt+P or the eye button in the editor title bar. |
| PictureCode sidebar |
A larger preview with the component highlighted on its page, and the list of every page it appears on. Tip: drag the view to the Secondary Side Bar (right side) to keep it beside your code. |
| Hover |
Hover a component name, such as <SearchHeader /> or an import, to see its picture. |
| CodeLens |
Each captured component gets a 📷 Preview · /search CodeLens above it. |
| Full size |
Click any picture to open it full size in an editor tab. |
Supported stacks
- React 16–19, using the fiber tree. Source files come from
_debugSource or _debugStack, with a name index as fallback. This covers Vite, CRA, Next.js and Remix.
- Vue 3, from component instances and
__file. Svelte 4/5 dev builds, from __svelte_meta. Angular, from ng.getComponent in dev mode.
- Routes are found in react-router objects and JSX, vue-router, Angular route arrays, TanStack
createFileRoute, and Next.js app/ and pages/. Nuxt, SvelteKit, Astro and Remix/React Router file routes are found too. On top of that, PictureCode follows links on each page (crawling), and adds any routes you list.
- Fake data comes from
axios/ky/ofetch/fetch-style calls, generic wrappers (get<T>(url)), typed service functions ((): Promise<T>), and URL constants (URLS.SEARCH). This includes calls inside the <script> blocks of .vue and .svelte files.
Requirements
PictureCode drives a headless Chrome, Edge, Brave or Chromium that is already on your computer. If none is found, it offers to install Chromium (npx playwright install chromium), or you can set picturecode.browserPath.
Configuration
All settings live under picturecode.*. The ones you'll most likely change:
| Setting |
Default |
|
devServerUrl |
auto |
Force a URL instead of detecting one. |
autoCapture |
onDevStartAndSave |
Or onDevStart, or manual. |
mock.mode |
fallback |
always: never call the backend. off: never fake data. |
routes / excludeRoutes |
|
Extra pages to capture, and pages to skip. /logout is always skipped. |
explore.enabled, explore.maxClicksPerPage |
true, 8 |
Click buttons that open modals, menus and tabs. |
sandbox.enabled, sandbox.maxPerRun |
true, 40 |
Render components that no page shows on their own. |
viewport.width/height, deviceScaleFactor |
1440×900, 1 |
Use 2 for sharp Retina pictures (about 4× the disk space). |
overlay.width, overlay.opacity |
240, 0.96 |
Minimap card size and opacity. |
storage |
extension |
project keeps pictures in .picturecode/ so you can commit and share them. |
.picturecode.json (optional, per project)
Run PictureCode: Create .picturecode.json to get a starter file with schema validation. It covers what settings can't:
{
"routes": ["/tickets/42", "/search?q=card"],
"excludeRoutes": ["/admin/*"],
// Seeded before your app boots: get past auth guards
"localStorage": { "token": "fake-token", "user": { "id": 1, "role": "admin" } },
"cookies": [{ "name": "session", "value": "abc" }],
// Fixed responses win over generated ones
"mocks": {
"GET /api/me": { "id": 1, "fullName": "Alex Johnson" },
"POST /tickets/search": { "status": 200, "body": { "items": [], "total": 0 } }
},
"waitForSelector": "[data-ready]",
// UI that needs several steps to reach
"interactions": [
{
"route": "/settings",
"name": "Preferences · step 2",
"steps": [
{ "click": "button:has-text('Edit preferences')" },
{ "click": "text=Continue" },
{ "wait": "text=Step 2" }
]
}
],
// Props for components rendered on their own (merged over the generated ones)
"props": {
"FilterRange": { "label": "Amount (TJS)", "min": 0, "max": 10000 }
},
// Set to false to stop PictureCode from clicking buttons
"explore": true
}
Steps use Playwright selectors: button:has-text('Filters'), [data-testid=open-filters], text=More, plain CSS. A step can click, hover, fill (["selector", "text"]), press a key, or wait (milliseconds or a selector).
Commands
PictureCode: Capture All Pages · Recapture Pages for Current File · Cancel Capture · Set Dev Server URL… · Open Picture Full Size · Toggle Minimap Picture · Delete All Captures · Show Log · Create .picturecode.json
Click the 📷 item in the status bar for a quick menu.
Development
npm install
npm run build # bundles dist/extension.js, dist/analysis-worker.js and dist/cli.js
npm test # unit tests (set PC_FIXTURE=/path/to/app to also test type analysis)
npm run package # type-checks and builds picturecode-x.y.z.vsix
node dist/cli.js <projectRoot> <devServerUrl> [outDir] # headless capture, handy for CI
Press F5 in VS Code with a launch.json of type extensionHost to debug.
How it works
- Detect. Shell integration reads your dev command's output for the
Local: URL. If that doesn't work, PictureCode probes your framework's default ports.
- Analyze (in a worker thread). The TypeScript compiler API finds HTTP calls and their response types, and turns each type into deterministic, realistic JSON. Property names guide the values:
email, avatarUrl, createdAt, price, status, and so on. Pagination flags like hasNext are set to false so infinite loaders stop.
- Capture. A headless browser opens each route. Only
fetch/XHR requests are intercepted, never your dev server's modules. PictureCode waits for the network to go quiet, fonts and lazy images to load, and animations to stop. Then it reads the component tree and takes a single screenshot.
- Slice. Component rectangles become WebP crops plus small thumbnails, all in the same browser. Nothing native ships with the extension.
Pictures stay on your machine. PictureCode only talks to your own dev server and to the backends your app already calls.
Known limits
- VS Code has no API for drawing inside the minimap, and it doesn't tell extensions how wide the editor is. The minimap card is an editor decoration placed just to the minimap's left. In files with very long lines, it is placed at column
overlay.estimatedColumns (default 96) instead; set that to the number of characters that fit across your editor. Long lines can run under the card. Hide it with Ctrl/Cmd+Alt+P, or use the sidebar.
- Pages behind real authentication need a seeded token or cookie in
.picturecode.json.
- Rendering a component on its own needs a Vite dev server, with React or Vue. With Next.js, webpack and Angular, components no page shows can only be reached through
interactions.
- A component that needs very specific props or context may fail to render alone. The sidebar then shows why, and you can give it props in
.picturecode.json.
Author
Made by Otajon Murodov (otajon.murodov.2002@gmail.com).
Bugs and ideas: GitHub issues.
Released under the MIT License.
| |