UI Components

Browse every JSX component mapped to SwiftUI with runnable examples.

Capsule

Capsule shape.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component capsule
// 

$render(
  <vstack frame="max">
    <capsule frame="50,30" color="blue"></capsule>
  </vstack>
);

Ellipse

Ellipse shape.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component ellipse
// 

$render(
  <vstack frame="max">
    <ellipse frame="50,30" color="blue"></ellipse>
  </vstack>
);

Ring

Circular progress ring.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: ring
//

$render(
  <vstack frame="max" padding="12">
    <ring value="0.68" thickness="10" color="#22c55e" trackColor="#e2e8f0" frame="72" />
  </vstack>
);

RoundedRect

Rounded rectangle block shape.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: roundedrect
//

$render(
  <vstack frame="max" padding="12">
    <roundedrect radius="12" color="#38bdf8" frame="140,60" />
  </vstack>
);

Badge

Compact pill for status tags.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: badge
//

$render(
  <vstack frame="max" padding="12" spacing="8">
    <badge text="PRO" background="#22c55e" color="#ffffff" />
    <badge text="BETA" background="#0f172a" color="#e2e8f0" />
  </vstack>
);

Chip

Outlined tag with optional border color.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: chip
//

$render(
  <hstack frame="max" padding="12" spacing="8">
    <chip text="Today" />
    <chip text="Focus" borderColor="#38bdf8" color="#0f172a" />
  </hstack>
);

HStack

Horizontal stack layout.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for component hstack
//

$render(
  <hstack>
    <text>First</text>
    <text>Second</text>
  </hstack>
);

Icon

SF Symbol icon with size and color.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: icon
//

$render(
  <hstack frame="max" padding="12" spacing="12">
    <icon systemName="bolt.fill" size="24" color="#fbbf24" />
    <icon systemName="moon.stars.fill" size="24" color="#6366f1" />
  </hstack>
);

Label

Text with SF Symbol icon.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: label
//

$render(
  <vstack frame="max" padding="12" spacing="8">
    <label title="Sunrise" systemName="sunrise.fill" color="#f59e0b" />
    <label title="Battery" systemName="battery.100" color="#22c55e" />
  </vstack>
);

Rect

Rectangle shape.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component rect
// 

$render(
  <vstack frame="max">
    <rect frame="50,30" color="blue"></rect>
    <rect frame="50,30" color="blue" corner="5"></rect>
  </vstack>
);

Stat

Metric display with title/value/subtitle.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: stat
//

$render(
  <hstack frame="max" padding="12" spacing="16">
    <stat title="Downloads" value="12.4k" subtitle="Today" color="#0f172a" />
    <stat title="Streak" value="18" subtitle="Days" color="#22c55e" />
  </hstack>
);

VStack

Vertical stack layout.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for component vstack
//

$render(
  <vstack>
    <text>First</text>
    <text>Second</text>
  </vstack>
);

ZStack

Overlay layout.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for component vstack
//

$render(
  <zstack>
    <text>First</text>
    <text>Second</text>
  </zstack>
);

Divider

Horizontal or vertical separator line.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: divider
//

$render(
  <vstack frame="max" padding="12">
    <text font="caption">Section A</text>
    <divider thickness="1" color="#94a3b8" />
    <text font="caption">Section B</text>
  </vstack>
);

GIF

Animated GIF (iOS only).

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Shape Template
// 
// Description: Shape Example
// 



$render(
  <vstack>
    <gif file="test" />
  </vstack>
);

Progress

Linear or circular progress indicator.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: progress
//

$render(
  <vstack frame="max" padding="12">
    <text font="caption">Loading</text>
    <progress value="0.72" total="1" color="#3b82f6" />
    <progress value="0.45" total="1" style="circular" color="#10b981" frame="32" />
  </vstack>
);

Circle

Circle shape.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component circle
// 

$render(
  <hstack frame="max">
    <circle frame="40,40" color="blue"></circle>
    <circle frame="40,40" color="blue" stroke="10"></circle>
    <circle frame="40,40" color="blue" stroke="10" trim="0.2"></circle>
    <circle frame="40,40" color="blue" stroke="10" trim="0.2" rotation="90"></circle>
  </hstack>
);

Date

Date rendering with formatting options.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component date
// 

$render(
  <vstack>
    <date font="caption" date="now" style="time" />
    <date font="caption" date="now" style="date" />
    <date font="caption" date="start of today" style="timer" />
    <date font="title" date={Date.now()} style="timer" />
  </vstack>
);

Line

Fixed-length line segment.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// component: line
//

$render(
  <vstack frame="max" padding="12" spacing="8">
    <line length="120" thickness="2" color="#94a3b8" />
    <hstack spacing="8">
      <line axis="vertical" length="24" thickness="2" color="#f59e0b" />
      <text font="caption">Vertical line</text>
    </hstack>
  </vstack>
);

Fragment

Group multiple children without wrapper.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
//

var aaa = (
    <>
        <text>a text</text>
        <text>a text</text>
    </>
)

$render(
    <vstack frame="max">
        {aaa}
        <text>a text</text>
    </vstack>
);

Gauge

Gauge visualization for numeric values.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component guage
// 

$render(
  <vstack frame="max">
    <gauge 
      type="system"
      value="0.6"
      text="hello"
      style="circular" // default
      >
    </gauge>
  </vstack>
);

Text

Text rendering with font/color modifiers.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component text
// 

$render(
  <vstack frame="max">
    <text font="title">Hello ScriptWidget</text>
    <text font="caption" color="red">
      Hello ScriptWidget
    </text>
    <text font="caption" background="blue" color="white">
      Hello ScriptWidget
    </text>
  </vstack>
);

Toggle

Toggle control for boolean state.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component capsule
// 


const onToggleClick = () => {
  console.log("toggle tapped");
}
const widget_size = $getenv("widget-size");

const value = Math.random() >= 0.5;

$render(
  <vstack frame="max">
    <toggle on={value} onClick="onToggleClick">
      <image systemName="mosaic.fill" />
      <text>{widget_size}</text>
    </toggle>
  </vstack>
);

Button

Tap action via AppIntent. Use action="reload" to refresh the widget.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component button
// 


const onButtonClick = () => {
  console.log("button tapped");
};
const widget_size = $getenv("widget-size");

$render(
  <vstack frame="max">
    <button onClick="onButtonClick">
      <image systemName="mosaic.fill" />
      <text>{widget_size}</text>
    </button>
    <button action="reload">
      <text>Refresh Widget</text>
    </button>
  </vstack>
);

Image

Image from asset, URL, or base64 data.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component image
// 

/*

    <image />
    <image systemName="mosaic.fill" />
    <image id="image0" />
    
    <image id="image" mode="fit" ratio="0.6" />
    <image id="image" mode="fill" frame="260,60" />

    <image id="image" mode="fill" clip frame="200,100"/>
    
    <image id="image" mode="fill" clip="rect" frame="200,100"/>
    <image id="image" mode="fill" clip="ellipse" frame="200,100"/>
    <image id="image" mode="fill" clip="circle" frame="200,100"/>
    <image id="image" mode="fill" clip="capsule" frame="200,100"/>

    <image id="image" mode="fill" corner="30" frame="200,100"/>
*/

$render(
  <vstack>
    <image url="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" frame="20,20"/>
    <image id="image" frame="260,60"/>
  </vstack>
);

Live Activity

Dynamic Island / Live Activity layouts.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component Live Activity and Dynamic Island
// 


// $render is also for lock screen live activity
$render(
    <vstack frame="max">
        <text>hello live activity</text>
    </vstack>
);


// $dynamic_island is for dynamic island
// on iPhone 14 Pro/ProMax and iOS16.1+
$dynamic_island({
    expanded: { // expanded is required , at least one of the four child below is required
        leading: <text>leading</text>,
        trailing: <text>trailing</text>,
        center: <text>center</text>,
        bottom: <text>bottom</text>,
    },
    compactLeading: <text>compactLeading</text>, // required
    compactTrailing: <text>compactTrailing</text>, // required
    minimal: <text>minimal</text>, // required
});

Chart

Chart visualization with sections.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component chart
// 

let data = [
    { label: "jan/22", value: 5 },
    { label: "feb/22", value: 4 },
    { label: "mar/22", value: 7 },
    { label: "apr/22", value: 15 },
    { label: "may/22", value: 14 },
    { label: "jun/22", value: 27 },
    { label: "jul/22", value: 27 },
]

$render(
    <vstack frame="max">
      <chart 
        type="bar" // required
        data={$json(data)} // required
        color="red" // optional , default black
        padding="20" // optional , general
        // hideLegend="true"
        // hideXAxis="true"
        // hideYAxis="true"
        >
      </chart>
    </vstack>
  );

Frame Attributes

Frame and sizing attributes.

// 
// ScriptWidget 
// https://xnu.app/scriptwidget
// 
// Usage for component attribute frame
// 

/*

 frame="max"
 frame="max,topLeading"
 frame="10,20"
 frame="10,20,topLeading"
 
 frame="max,20"
 frame="10,max"
 frame="max,20,topLeading"
 frame="10,max,topLeading"


 the "topLeading" represent alignment, could be one of the values below:
    "center"
    "leading"
    "trailing"
    "top"
    "bottom"
    "topLeading"
    "topTrailing"
    "bottomLeading"
    "bottomTrailing"
*/
$render(
  <vstack frame="max">
    <rect frame="50,30" color="green"></rect>
    <rect frame="50,30" color="blue" corner="5"></rect>
  </vstack>
);

HGrid

Horizontal grid layout.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// grid component
// 

/*
iOS
{ type: "adaptive", min: "30"},
{ type: "fixed", value: "30"},
{ type: "flexible"},

macOS
{ type: "adaptive", min: "30", max: "100"},
{ type: "fixed", value: "30"},
{ type: "flexible"},

*/

// {} will default to { type: "flexible"},
// let columns = [
//     {},
//     {},
//     {},
//     {},
// ]

// let columns = [
//     { type: "fixed", value: "30"},
//     { type: "fixed", value: "30"},
//     { type: "fixed", value: "30"},
// ]

let columns = [
    { type: "adaptive", min: "70", max: "100"},
]

$render(
  <vstack frame="max">
    <hgrid columns={$json(columns)}>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
    </hgrid>
  </vstack>
);

VGrid

Vertical grid layout.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// grid component
// 

/*
iOS
{ type: "adaptive", min: "30"},
{ type: "fixed", value: "30"},
{ type: "flexible"},

macOS
{ type: "adaptive", min: "30", max: "100"},
{ type: "fixed", value: "30"},
{ type: "flexible"},

*/

// {} will default to { type: "flexible"},
// let columns = [
//     {},
//     {},
//     {},
//     {},
// ]

// let columns = [
//     { type: "fixed", value: "30"},
//     { type: "fixed", value: "30"},
//     { type: "fixed", value: "30"},
// ]

let columns = [
    { type: "adaptive", min: "70", max: "100"},
]

$render(
  <vstack frame="max">
    <vgrid columns={$json(columns)}>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
        <circle color="red"/>
    </vgrid>
  </vstack>
);

Custom Component

Define reusable JSX components with $component.

//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Custom Component
// Thanks for Reina for telling me this good idea
// 


// Custom component is a JavaScript function
// with one parameter composed with : prop1,prop2... and children
// As a convention, 
// the first letter of custom component's name must be uppercase.
// For example : LeftAlign , MyCustomComponent ... etc...


// prototype:
// 1. new style
// const MyComponent = ({prop1, prop2, children}) => { /* ... */ }
// 
// 2. old style
// const MyComponent = (props) => { 
//   let prop1 = props.prop1;
//   let prop2 = props.prop2;
//   let children = props.children;
// }

const LeftAlign = ({ message, children }) => {
    return (
        <hstack>
            {children}
            <text> ({message}) </text>
            <spacer />
        </hstack>
    )
}
const RightAlign = ({ message, children }) => {
    return (
        <hstack>
            <spacer />
            <text> ({message}) </text>
            {children}
        </hstack>
    )
}
$render(
    <vstack
        background="blue"
        frame="max,center"
    >
        <LeftAlign message="L">
            <text> this text left aligned</text>
        </LeftAlign>
        <RightAlign message="R">
            <text> text right aligned</text>
        </RightAlign>
    </vstack>
);