What is the Object Display standard on Sui?
November 9, 2026
Object Display is Sui's standard for describing how objects of a type should look off-chain. The type's owner publishes a Display<T> object holding key-value templates such as name, description and image_url, and values like "{name} (lvl. {level})" get filled in from each object's fields. Wallets, explorers and marketplaces read that template instead of guessing from raw struct fields. The current version, Display V2, lives in sui::display_registry: there is exactly one shared Display<T> per type, at an ID derived from the system registry at 0xd.
How it works
You don't put metadata fields on every object. The display rules live in one place:
Display<T>is a shared object whose ID is derived from theDisplayRegistry(address0xd) and the typeT. Anyone, including full nodes, can work out that ID offline, so no event scanning is needed.- A separate owned
DisplayCap<T>authorizesset,unsetandclear. Change the template once and every object of that type renders differently, with no need to migrate the objects themselves. - If a type has no display, clients just show the raw fields.
The suggested keys are name, description, link, image_url, thumbnail_url, project_url and creator. The standard doesn't enforce any of them, so you can add your own.
Creating a display
The registry is a shared object, and init only receives the one-time witness and TxContext. So you create the display with a separate call right after publishing. This follows the Move Book example:
module book::arena;
use std::string::String;
use sui::display_registry::{Self, DisplayRegistry};
public struct Hero has key {
id: UID,
class: String,
level: u64,
}
/// Call once after publishing; a second call aborts because
/// the registry holds a single Display per type.
entry fun create_display(registry: &mut DisplayRegistry, ctx: &mut TxContext) {
let (mut display, cap) = display_registry::new<Hero>(
registry,
internal::permit(),
ctx,
);
display.set(&cap, "name", "{class} (lvl. {level})");
display.set(&cap, "description", "One of the greatest heroes of all time.");
display.set(&cap, "link", "https://example.com/hero/{id}");
display.set(&cap, "image_url", "https://example.com/hero/{class}.jpg");
display.share();
transfer::public_transfer(cap, ctx.sender());
}display_registry::new takes an internal::Permit<T>, which only the module that defines T can create. To create a display from outside that module, use new_with_publisher, which takes your Publisher object instead. Call the function with the registry's ID:
sui client call --package <PACKAGE_ID> --module arena --function create_display --args 0xdTemplate syntax
Values are format strings. {field} inserts a field, and dot paths reach nested structs ({metadata.description}). V2 adds more:
- Indexing:
{items[0u64]}. Numeric literals always need a type suffix. - Fallbacks:
{display_name | name | 'Anonymous'} - Transforms:
{created_at:ts}(ISO 8601 from Unix milliseconds),:hex,:base64,:json,:url - Dynamic fields:
{parent->['color']}for a dynamic field,{parent=>['hat']}for a dynamic object field. Each load counts against a budget of 8 object loads by default (->costs one,=>costs two).
Use {{ to output a literal brace.
V1 displays and migration
The original sui::display module (V1) used owned Display<T> objects found through DisplayCreated events, and a type could have several of them. A system migration copied every existing V1 display into a V2 Display<T> with cap_id set to none. To manage a migrated display, claim its cap with display_registry::claim (which consumes your V1 object) or claim_with_publisher. You can then delete the old object with delete_legacy. The Display V2 announcement said the V1 sui::display::new APIs would stop being callable after July 31, 2026, so new code should use only display_registry.
Gotchas
- Make the setup function
entry, notpublic. A later upgrade can drop anentryfunction, butpublicsignatures are frozen. - Keep the
DisplayCapsafe. Whoever holds it controls how your type looks in every wallet. - Rendering happens in RPC and indexers, not on-chain. Check the result in an explorer after you set the fields.
Sources
- Creating Displays, Sui documentation
- Object Display V2 Syntax, Sui documentation
- Object Display, The Move Book
- Display V2: How Sui Objects Present Themselves to the World, Sui blog
- display_registry.move, Sui framework source