Widget structure guide

Published May 14, 2026, 4 min read

How to build a widget that Appinapp can read, install, and run.

If you want to build widget with AI send this is schema link on top of prompt as this is widget scheme: {schema-link}

Repo naming

Your widget is a public GitHub repo whose name ends in .widget.
Clock.widget
When the app installs it, it git clones the repo into the widget folder
(~/.appinapp/) as Clock.widget/. The folder name (minus .widget) becomes
the widget's name.

Folder layout

Every widget folder must contain these four files at its root:
Clock.widget/
├── widget.json      # manifest (metadata)
├── index.jsx        # widget code (index.js also accepted)
├── screenshot.png   # preview image for the gallery
└── README.md        # docs: what it does, how to customize
A widget is only recognized if the folder ends in .widget and contains an
index.js or index.jsx. Without index, the app skips the folder.

widget.json (manifest)

Plain JSON. All four fields required:
{
  "name": "Widget name",
  "description": "A little description about widget features.",
  "author": "author name",
  "email": "author-example@gmail.com"
}
FieldPurpose
nameDisplay name in the gallery
descriptionShort feature blurb
authorYour name / handle
emailContact for the listing

index.jsx (the code)

The app loads index.jsx, compiles JSX in place, and reads its exports. You
export a default React component, plus optional config exports.

Imports — limited environment

Only two modules resolve inside a widget:
import React from "react";             // → window.React
import { Clock } from "lucide-react";  // → window.Lucide
Any other import throws Module <name> not found in widget environment. No
npm deps. Bring your own logic in the file.

Minimal widget

import React from "react";

export default function Widget() {
  return <div className="widget">Hello</div>;
}

Widget props

The app provides you output, error, run:
export default function Widget({ output, error, run }) {
  return <div></div>;
}
PropWhat it is
outputstdout string from your command (see below)
errorerror if the command failed
runasync (cmd) => stdout — run a shell command in the widget dir

Optional exports (widget config)

// Shell command run on mount; its stdout arrives as the `output` prop
export const command = "date +%H:%M:%S";

// Re-run `command` every N milliseconds
export const refreshFrequency = 1000;

// CSS injected as a <style> tag while the widget is mounted
export const className = `
  .widget { font-family: monospace; color: #fff; }
`;

// Initial window geometry (logical pixels)
export const windowWidth = 200;
export const windowHeight = 80;
export const windowTop = 40;
export const windowLeft = 40;
ExportTypeEffect
defaultComponentThe widget UI (or export render)
commandstringShell command, stdout → output prop
refreshFrequencynumberms interval to re-run command
classNamestringCSS injected as <style>
windowWidth/HeightnumberInitial window size
windowTop/LeftnumberInitial window position

Full example

import React from "react";

export const command = "date +%H:%M:%S";
export const refreshFrequency = 1000;
export const windowWidth = 200;
export const windowHeight = 80;

export const className = `
  .clock { font: 600 28px monospace; color: #fff; padding: 12px; }
`;

export default function Clock({ output, error }) {
  if (error) return <div className="clock">err</div>;
  return <div className="clock">{output || "--:--:--"}</div>;
}

screenshot.png

A preview image shown in the app gallery. Capture your widget running.

README.md

Describe what the widget does and how to customize it. Shown to users who open
your widget's source.

Submit the repo URL on the Submit page. The app validates these
requirements automatically before listing.