Widget Examples

will there be a way to define multiple tap targets for the medium and large widgets?
maybe something like WidgetText.url property (for consistency with the ListWidget.url property)?

Unfortunately not. iOS decides the update cycle. It seems to update less frequently if your iPhone is in energy saving mode.

3 posts were split to a new topic: Random Trello Card

Widgets update based on a number of things, usually every 5-10 minutes
However it changes based on the battery and that sort of thing.

1 Like

Hi, I was wondering if someone could help me. I have made a widget and I want to add a red border around it. I typed in

w.borderColor = Color.red()
w.borderWidth = 10

And nothing happened. Can anyone help?

Showing the next game of your favorite soccer team (when, opponent, competition) using the football-data.org API (using Ajax as an example):

1 Like

I love widgets !

I’ve created three widgets

One that shows the amount of traffic, accidents,…
Another one that shows people who are missing

The last one is K POP news

1 Like

I was thinking it would be a fun project to create a widget that rotated through a Photo folder. Does scriptable allow for getting images from a folder in Photos?


I am trying to create a widget that displays my reminders for today (and tomorrow if there is space left) I have based it on a previous post from here. Everything is working fine except: I want the widget to change appearance the i am in dark mode. It works when running the script inside the app, but it dose not work outside of scriptable, when it is used as a widget.

The code I am using to switch between dark and light looks something like this:

if ( Device.isUsingDarkAppearance() ) {
  // dark mode appearance settings
  } else {
  // normal settings

I have also included the full code which looks like this:

const NOW = new Date();

const TITLE_FONT = Font.boldSystemFont(11);
const BODY_FONT = Font.semiboldRoundedSystemFont(12);

if ( Device.isUsingDarkAppearance() ) {
  var BG_COLOR = new Color("#1C1C1E");
  var TITLE_COLOR = new Color("#9E9E9E");
  var OVERDUE_COLOR = new Color("#FE4639");
  var TASK_COLOR = new Color("#FFFFFF");
  var NO_OVERDUE_COLOR = new Color("#2FD15D");
  } else {
  var BG_COLOR = Color.white();
  var TITLE_COLOR = new Color("#8C8C8C");
  var OVERDUE_COLOR = new Color("#FD3F32");
  var TASK_COLOR = new Color("#000000");
  var NO_OVERDUE_COLOR = new Color("#13C759");

const today = new Date()
const tomorrow = new Date(today)
  tomorrow.setDate(tomorrow.getDate() + 1)
const tomorrow2 = new Date(today)
  tomorrow2.setDate(tomorrow2.getDate() + 2)

// Utils

const compareReminderDuedates = (reminderA, reminderB) =>
  reminderA.dueDate - reminderB.dueDate;
const sortRemindersByDuedateAsc = reminders =>

/** Round date down to 00:00 */
const stripTime = date => new Date(new Date(date).setHours(0, 0, 0, 0));

/** d1 - d2: will be negative if d2 > d1 */
const daysBetween = (d1, d2) => {
  const differenceMs = stripTime(d1).getTime() - stripTime(d2).getTime();
  return Math.floor(differenceMs / 86400000);

const getOverdueTasks = async () => {
  const all = await Reminder.allIncomplete();
  return sortRemindersByDuedateAsc(
    all.filter(task => task.dueDate && task.dueDate < today)

const getTodayTasks = async () => {
  const all = await Reminder.allIncomplete();
  return sortRemindersByDuedateAsc(
    all.filter(task => today <= task.dueDate && task.dueDate < tomorrow)

const getTomorrowTasks = async () => {
  const all = await Reminder.allIncomplete();
  return sortRemindersByDuedateAsc(
    all.filter(task => tomorrow <= task.dueDate && task.dueDate < tomorrow2)

// overdue tasks

const getWidget = async () => {
  const overdueTasks = await getOverdueTasks();
  const todayTasks = await getTodayTasks();
  const tomorrowTasks = await getTomorrowTasks();
  const widget = new ListWidget();
  widget.backgroundColor = BG_COLOR;
  const dueNum = overdueTasks.length + todayTasks.length
  if (dueNum) {
    var title = widget.addText(`HEUTE   ${dueNum}`);
    var title = widget.addText(`HEUTE`);
  title.textColor = TITLE_COLOR;
  title.font = TITLE_FONT;

  if (overdueTasks.length) {
    overdueTasks.slice(0, MAX_TASKS_SHOWN).forEach(({ title, dueDate }) => {
      const task = widget.addText(`| ${title}`);
      task.textColor = OVERDUE_COLOR;
      task.font = BODY_FONT;
      task.lineLimit = 1;
  } else {
  MAX_TASKS_SHOWN = MAX_TASKS_SHOWN - overdueTasks.length 
  if (MAX_TASKS_SHOWN > 0) {
    if (todayTasks.length) {
      todayTasks.slice(0, MAX_TASKS_SHOWN).forEach(({ title, dueDate }) => {
        const task = widget.addText(`| ${title}`);
        task.textColor = TASK_COLOR;
        task.font = BODY_FONT;
        task.lineLimit = 1;
    } else {}
  } else {}
  if (dueNum) {
  } else {
    const noTasks = widget.addText("Alles erledigt.");
    noTasks.textColor = NO_OVERDUE_COLOR;
    noTasks.font = BODY_FONT;

// tomorrow

//  const tomorrowTasks = Reminder.allDueTomorrow();

  MAX_TASKS_SHOWN = MAX_TASKS_SHOWN - todayTasks.length - 2
  if (MAX_TASKS_SHOWN > 0){
    if (tomorrowTasks.length) {
      const title = widget.addText(`MORGEN   ${tomorrowTasks.length}`);
      title.textColor = TITLE_COLOR;
      title.font = TITLE_FONT;
      tomorrowTasks.slice(0, MAX_TASKS_SHOWN).forEach(({ title, dueDate }) => {
        const task = widget.addText(`| ${title}`);
        task.textColor = TASK_COLOR;
        task.font = BODY_FONT;
        task.lineLimit = 1;
    } else {}
  } else {}

  return widget;

(async () => {
  const widget = await getWidget();
  if (config.runsInWidget) {
  } else await widget.presentSmall();


Watching - this is a great idea - hope it’s implementable!

I’ve been a bit obsessed with this on the Mac

Would you mind sharing your script?

A combination script and couple of shortcuts to check on CitiBike docks in particular locations (where I am, where I am going). This is the bike share system in New York City. Would probably work for other systems that use the GBFS standard. Will probably add some more interesting functionality for on-tap.

21 posts were split to a new topic: Custom Calendar Widget

I would like to see an example of the tapping functionality. Also I have a question, is the (+ Note) button tappable?

I had the same issue with my xkcd widget. I made a separate function to solve it. it’s an async function though but not too much of an issue.

async function isUsingDarkAppearance() {
  const wv = new WebView()
  let js ="(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)"
  let r = await wv.evaluateJavaScript(js)
  return r
1 Like

A widget showing a random painting from the Metropolitan Museum of Art.
If you click on it you see the details in your browser.

Code here

//Department ID = 11 is paintings ; use other if you wish

const url = 'https://collectionapi.metmuseum.org/public/collection/v1/search?hasImages=true&departmentId=11&q=Painting'
const req = new Request(url)
const res = await req.loadJSON()
const max = res.total-1
console.log("Max: "+max);
const min = 0
const random = Math.floor(Math.random() * (max - min + 1)) + min
console.log("Random: "+random);
const picked = res.objectIDs[random]
console.log("Picked: "+picked);
const req2 = new Request('https://collectionapi.metmuseum.org/public/collection/v1/objects/'+picked)
const res2 = await req2.loadJSON()
const i = new Request(res2.primaryImageSmall);
const img = await i.loadImage();

let widget = createWidget(img, res2.objectURL)
if (config.runsInWidget) {
  // create and show widget
else {

function createWidget(img, widgeturl) {
  let w = new ListWidget()
  w.backgroundColor = new Color("#1A1A1A")
  w.backgroundImage = img
  w.url = widgeturl
  return w

Can you please share your script?

For the guys asking about the code: I did not post it because this was just a quick hack and not clean code.

On top of that I currently have an issue with this script after cleaning up the code a bit by putting some common code in a library. The import of that library sometimes fails due to lack of permissions when viewing the widget (workaround: start the Scriptable App).

const soccer = importModule('lib/Soccer')

@simonbs Is this a widget issue? A Scriptable bug? Or did I do something wrong?

I might post a cleaned up variant later, but if not: the crucial part for people that want to write a similar widget is a GET request with this URL:


Here 678 is the ID for Ajax; you would need the ID of your own favourite team in your script.

(one of the things I still need to improve is that I currently just take the first entry in the returned array; but it would be safer if I sort that array on the match date first, because I don’t know whether correct order is guaranteed in the result)

PS: Additionally I would want to display the team’s club logos; so I’m looking forward to the stack view feature Simon announced on Twitter!

1 Like

I created a widget for tracking pending reviews on Wanikani (a kanji learning site).

It shows my current level and the number of reviews I need to clear. Tapping the widget opens wanikani.com in the browser for me to get to work.

// web constants
const apiKey = "API-KEY-GOES-HERE"
const auth = "Bearer " + apiKey
const waniKaniRev = "20170710"

const userURL = "https://api.wanikani.com/v2/user"
const summaryURL = "https://api.wanikani.com/v2/summary"
const imageURL = "https://adomainnamegoeshere.com/apathgoeshere/animagegoeshere.png"

const waniKaniURL = "https://wanikani.com/"

// create the web request to the wanikani api
let userRequest = new Request(userURL)
userRequest.headers = {"Authorization": auth, "Wanikani-Revision": waniKaniRev};

// get the current user level from the user account info
let user = await userRequest.loadJSON()
let myLevel = user.data.level

// get the current number of pending reviews from the summary info
userRequest.url = summaryURL
let summary = await userRequest.loadJSON()
let myReviewCount = summary.data.reviews[0].subject_ids.length

// create the widget
let waniKaniWidget = new ListWidget()

// set the background image and color
waniKaniWidget.backgroundColor = new Color("#2c94f5")
let waniKaniImage = await loadImage(imageURL)
waniKaniWidget.backgroundImage = waniKaniImage

// add the level title and value
let levelLabel = waniKaniWidget.addText("Level")
levelLabel.font = Font.boldSystemFont(16)
levelLabel.textColor = new Color("#ffffff")

let levelNotice = waniKaniWidget.addText(myLevel.toString())
levelNotice.font = Font.boldSystemFont(40)
levelNotice.textColor = new Color("#00ff2f")


// add the reviews pending title and value
let reviewsLabel = waniKaniWidget.addText("Reviews")
reviewsLabel.font = Font.boldSystemFont(16)
reviewsLabel.textColor = new Color("#ffffff")

let reviewsCount = waniKaniWidget.addText(myReviewCount.toString())
reviewsCount.font = Font.boldSystemFont(40)
reviewsCount.textColor = new Color("#dafd32")

waniKaniWidget.url = waniKaniURL

if (config.runsInWidget) {	
} else {	

async function loadImage(imageUrl) {
  let req = new Request(imageUrl)
  let image = await req.loadImage()
	if (image != null) {
		return image
	} else {
		console.log("something happened")
1 Like

J, this is an amazing widget! I’ve been playing with it, but for the life of me, I can’t solve one problem. (I have close to no experience with this) When the system is in light mode, the font color flips to black on black. I’ve tried setting the color a few different ways and selecting specific fonts, but it changes to black on black every time the system theme changes. Any idea how to keep it white on black?