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 asthis 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
(
the widget's name.
git clones the repo into the widget folder(
~/.appinapp/) as Clock.widget/. The folder name (minus .widget) becomesthe 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 anindex.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"
}
| Field | Purpose |
|---|---|
name | Display name in the gallery |
description | Short feature blurb |
author | Your name / handle |
email | Contact for the listing |
index.jsx (the code)
The app loads
export a default React component, plus optional config exports.
index.jsx, compiles JSX in place, and reads its exports. Youexport 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
npm deps. Bring your own logic in the file.
import throws Module <name> not found in widget environment. Nonpm 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>;
}
| Prop | What it is |
|---|---|
output | stdout string from your command (see below) |
error | error if the command failed |
run | async (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;
| Export | Type | Effect |
|---|---|---|
default | Component | The widget UI (or export render) |
command | string | Shell command, stdout → output prop |
refreshFrequency | number | ms interval to re-run command |
className | string | CSS injected as <style> |
windowWidth/Height | number | Initial window size |
windowTop/Left | number | Initial 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.
your widget's source.
Submit the repo URL on the Submit page. The app validates these
requirements automatically before listing.
requirements automatically before listing.