Images

Ship images and icons with a plugin.

Import an image like a module. A PNG, JPEG, WebP or GIF gives an asset id for source props. An SVG gives an icon component.

Quick Start

import { Image } from 'react-native';
import star from '../assets/icons/star.png';

export function Rating() {
	return <Image source={star} style={{ width: 24, height: 24 }} />;
}

ubd build copies the file into dist/ and lists it in the built manifest. Unbound registers it with Discord's asset registry when your plugin starts. The id works anywhere Discord takes a source:

import { Discord } from '@unbound-app/api/metro/components';
import star from '~/images/star.png';

<Discord.TableRow label='Favourites' icon={<Discord.TableRowIcon source={star} />} />;

Images can live anywhere. Import with a relative path or ~/ (which points at src/).

Reference

Import or callGives
import id from './star.png' (also .jpg, .jpeg, .webp, .gif)The asset id, a number
import Star from './star.svg'An icon component
assets.getIdByName(name, type?)The id, or undefined. Your plugin's names first, then Discord's
assets.getByName(name, type?)The asset record, or undefined
assets.getById(id)The asset record, or undefined
assets.find(filter)The first asset filter accepts
assets.getAll()Every registered asset
assets.register(options)Registers a runtime image. Returns { id, unregister }
assets.onAssetRegistered(listener)Calls listener(asset, id) for each later asset. Returns a remover

Other file types fail the build, naming the file. Full signatures: assets in the SDK reference.

An imported image always ships. Every asset and listener your plugin registered is removed when it stops. An unregistered id draws nothing.

Screen Densities

Put larger versions beside the file with @2x or @3x. Import the plain name, and Unbound picks the right file.

assets/icons/star.png      24 × 24
assets/icons/[email protected]   48 × 48
assets/icons/[email protected]   72 × 72

The build reads each image's size, so <Image source={star} /> needs no style. Shipping only @3x works.

SVGs

An SVG import is an icon component like Discord's own WrenchIcon. Pass the component to IconComponent props, and an element to icon props:

import { Discord } from '@unbound-app/api/metro/components';
import Star from '../assets/star.svg';

<Discord.TableRow label='Favourites' icon={<Discord.TableRowIcon IconComponent={Star} />} />;
<Discord.ActionSheetRow label='Favourite' icon={<Discord.ActionSheetRow.Icon IconComponent={Star} />} />;
<Discord.IconButton icon={<Star />} />;

Unbound's APIs take an asset id, an element or a component:

import { toasts, ui } from '@unbound-app/api';
import Star from '../assets/star.svg';

toasts.show({ title: 'Starred', icon: Star });
<ui.Row label='Favourites' icon={Star} />;
PropTakesThe icon gets
TableRowIcon, ActionSheetRow.Icon IconComponentA componentsize and color for the row
A settings entry's IconComponentA componentThe same, through TableRowIcon
MenuItem, ContextMenuItem IconComponentA componentsize: 'sm', and color in a context menu
TableRow, ActionSheetRow, Button, IconButton iconAn elementNothing. Own size and colour
ui.Row, ui.SwitchRow, toasts.show icon, a toast button's iconAn id, element or componentTableRowIcon, or md in a toast

Size and Colour

PropValues
size'xxs' (12), 'xs' (16), 'sm' (18), 'md' (24, default), 'lg' (32), a number, or 'custom' to size with style. width and height win
colorA theme token such as Theme.colors.TEXT_LINK, or a colour. Default: the theme's icon colour

A one-colour SVG (default black fill included) is tinted by color. One with two or more colours, gradients, masks, <style> or class keeps its colours. To show a one-colour SVG in its own colour, pass that colour.

Build Behaviour

  • Strips comments, doctype, editor metadata (<metadata>, sodipodi:*, inkscape:*) and whitespace between tags.
  • Fails on malformed XML or a root that isn't <svg>.
  • Warns above 32 KB. Simplify it or export a PNG.
  • Inlines each file once, however often it is imported. Nothing loads until first render.
  • Never registers an SVG as an asset. For a source prop, use a PNG.

Lookup by Name

An image's name is its path without extension or scale suffix. Under assets/ it starts inside that folder: assets/icons/star.png is icons/star. Elsewhere it starts at the plugin root: src/images/star.png is src/images/star.

import { assets } from '@unbound-app/api';

const star = assets.getIdByName('icons/star');

Two plugins can both have icons/star without conflict.

Runtime Images

assets.register registers an image made while running, such as a data: URI or a file written with fs.

import { assets, fs } from '@unbound-app/api';
import { Image } from 'react-native';

const avatar = assets.register({
	name: 'avatar',
	path: `${fs.documentsPath}/MyPlugin/avatar.png`,
	width: 32,
	height: 32,
	type: 'png',
});

<Image source={avatar.id} />;
OptionWhat it does
nameThe lookup name. Registering a name again reuses its id
uri or pathThe image at scale 1: a URI (data:, https:) or an absolute path
width, heightSize in points
typeFile type, such as png
scalesOther densities: { 2: uri, 3: uri }

While Developing

ubd dev rebuilds and pushes when you add, edit or replace an image. Files are named by content, so a replaced image never shows a cached copy.