Mini Apps: the experimental embed already works — let's turn it into a real feature

Hi everyone :waving_hand: — first post here, and it’s a long one, sorry. I went down a rabbit hole this weekend and came out with something I think is worth sharing.
WHAT DO YOU RECOMMEND?

Graduate AnytypeMiniApp out of experimental and make it a real feature: small interactive apps written inside a page, whose state is stored in the object itself.

It already exists. It landed in 0.55.7-alpha through the mini-app-test branch, it ships in stable 0.56.x, and it sits behind Sudo → Experimental features. It appears in no changelog and I could not find a single thread about it here. So I am not asking you to build it — I am asking you to finish it.

HOW COULD IT BE DONE?

How it works today, for anyone who wants to try: add an “Anytype Mini App” embed block, then two sibling code blocks — one set to HTML (the app), one set to JSON (its state). The app reads window.__ANYTYPE_STATE__, listens for the anytype:state event, and writes back through window.__ANYTYPE_API__.setState().

// read
let state = window.\__ANYTYPE_STATE_\_ || { items: \[\] };

// react to changes coming from Anytype (another device, undo, manual JSON edit)
window.addEventListener('anytype:state', e => {
  state = e.detail || { items: \[\] };
  render();          // never call setState() from here — infinite loop
});

// write — Anytype serialises this into the sibling JSON code block
window.\__ANYTYPE_API_\_.setState(state);

One catch worth documenting: if the JSON block contains {}, __ANYTYPE_STATE__ is {} — truthy, so the || fallback above never fires and your app crashes on the first missing key. Rebuild a valid shape from whatever arrives rather than trusting it.

Full source: shopping list · training log

What is missing, roughly in order of difficulty:

  1. Discoverability. Right now you get an empty rectangle and a two-block convention nobody can guess. It needs a starter template on creation, an example, and an error message when the convention isn’t met.
  2. Presentation. Both code blocks stay visible under the rendered app. They should be collapsed or hidden by default.
  3. Data model. The middleware Processor enum stops at Spotify = 24, so values 25–27 aren’t in the model. Nothing renders on iOS or Android, and I’d assume no guarantees around import/export or snapshots.
  4. Concurrency. The JSON state lives in a CRDT text block. Two devices writing at once can merge into invalid JSON.
  5. Trust. The hard one. Blocks sync, so code written by one member of a shared space runs on every other member’s machine when they open the page. This needs a permission and consent model before it could ever be on by default. I’ve sent the technical details to [email protected] rather than posting them here.

REAL WORLD USE CASES

Here is what made me write this. I currently depend on Bring! for shopping lists, Habitica for habit tracking, and several self-hosted services for equally simple things. Each one is another account, another privacy policy, another service to maintain and back up. These are tiny apps — a list, a counter, a form and some arithmetic — and yet each one comes with its own silo.

The problem isn’t that they’re bad. It’s that none of them should need to exist separately. My shopping list has no business living on someone else’s server, unencrypted, outside my space, when Anytype already does the hard part: end-to-end encryption, sync, sharing, offline, history.

So I rebuilt the shopping list as a mini app. Illustrated product catalogue, one tap to add, quantities, notes, a “in the cart” section. It syncs across my devices and I can share it with my partner by sharing the object — encrypted, no third-party account, no server to maintain.

I also built a full training log: set entry, illustrated exercise library, estimated 1RM curve, weekly volume, rest timer, training heatmap. Plain JavaScript, no CDN, fully offline.

Two services removed from my life in a weekend. That is the point.

What this enables that Anytype can’t do otherwise: computation and interaction that a database of objects doesn’t provide. A page of numbers will never give you a progression curve, a rest timer, or two-tap entry.

The same mechanism covers a lot of ground:

  • Finance — budget tracking with categories and charts, loan schedules, savings calculators
  • Cooking — recipes that scale ingredients to the number of guests, per-step timers, generated shopping lists
  • Health — habit tracking, mood journals, sleep logs
  • Work — domain calculators, conditional checklists, quote estimators
  • Personal — collections with scoring rules, trip planners, reading trackers

And the part that makes all of this interesting: a mini app is an object. So you can send it to someone, publish it, drop it in a shared space. One person writes a good budget tracker, shares it, and anyone can use it in their own space with their own data. That gives you a community library of apps with no extension store and no install step — just Anytype objects being passed around. Nothing else in the app can do that today.

RECOMMENDED ALTERNATIVES

  • Existing embed blocks (Mermaid, Chart, Kroki) render content, but hold no state and offer no interaction.
  • The Local API lets you build external tools, but then you leave Anytype and lose the property that the data lives in the object.
  • An object database with relations covers storage, but no computation and no custom interface.

None of these replaces what the mini app already does. And to pre-empt the obvious answer: an external plugin does not put the data in the object. All the value — encryption, sync, history, export, sharing — comes from the state being a block rather than a file on the side. No plugin architecture gives you that for free.

ADDITIONAL CONTEXT

What I find elegant about the current implementation, and worth saying out loud: the state isn’t in localStorage or a side file. It’s an ordinary block inside the object. So it inherits end-to-end encryption, sync, offline editing, version history and export. No new storage layer, no migration. That’s the difference between a gadget and something you’d trust with real data.

The broader point is that this mechanism lets you build small cross-device synced interfaces on top of your own data. Not apps in the extension-store sense — just objects that know how to show a form, run a calculation, and remember the result.

Put together, that means centralising daily life in one encrypted app instead of scattering it across a dozen micro-services. That is exactly the promise of “the everything app”, applied to small tools rather than documents.

My questions: is this meant to graduate, or was it a spike? Is it the same track as “API and plugins” on the roadmap? If there’s interest, I’m happy to help — the mechanical parts (proto values, docs, a starter template), or the trust and permission model.

Related: Plugin architecture discussion, Plugins / extensions, Generic iframe embedding.

Either way, thanks for building this thing. I’ve been using Anytype daily for a while and this is the first time I’ve felt like I could extend it rather than work around it. Happy to answer questions or share more of the code.

Welcome to the forum! That’s a nice read :slight_smile:

Do these mini apps live completely inside themselves or do they read and or write in other objects/blocks? So if any mini app is deleted will the information also be deleted or is it stored somewhere else for example in a query or another object?

I just stumbled across Mini-Apps, because I discovered the Anytype community chat channel by chance, where they are on display.

Very interesting indeed.

However, what about security? Isn’t it possible to put malware code in the app?

Maybe that is the reason why they are not rolled out yet. There should be some sort of sandboxing or rights management in place beforehand.

This is precisely why we have not widely rolled out mini apps. Somebody could share a mini app with malicious code that takes advantage of user-injected space access. We love the experimentation from our testers of Bobrik, however we don’t recommend regular users of Anytype to use mini apps just yet. If you want to, at least do it in a non-sensitive space, inspect the code, and take your own risk precautions.

Thanks! :slight_smile:

Completely inside themselves — a mini app cannot read or write any other object.

The data lives in a plain JSON code block sitting right under the app, at the same indentation level as the embed block and the HTML block. All three are siblings. When the app calls setState(), Anytype writes the serialised state into that code block as text — it’s the same operation as you typing in it yourself. You can open it, read it, and edit it by hand.

So yes: delete the object and the data goes with it, exactly like any other object. Nothing is stored anywhere else, and there’s no hidden database. The upside is that it inherits everything a normal block gets — version history, undo, sync, offline, export, encryption. If you break your state, object history will restore it.

The flip side is the real limitation today: the app is an island. It can’t read your Task objects, your relations, or a query. So my training log doesn’t know about anything else in my space, and its data doesn’t show up in any query. That’s the thing I’d most like to see next — a permissioned read API where an app declares what it wants access to and you grant it. Right now the only surface exposed is setState() on its own state, nothing more.

@Zak-from-Zork you’re right, and @kaye just confirmed it — that’s exactly why it’s still hidden, and I think that’s the correct call.

I raised it as point 5 above without going into specifics. Now that the team has opened the topic publicly, here’s the shape of what I think would need to exist, at a level that doesn’t hand anyone a recipe:

Provenance-based consent. The distinction that matters isn’t “is this code dangerous”, it’s “did I write it”. Code I typed myself, in my own space, is not a threat model. Code that arrived over sync, authored by someone else, is. Auto-run should apply only to the first case; the second should show “this page contains an app written by X — run it?” and default to no.

Declared capabilities, default deny. The app declares what it needs — persist its own state, reach the network, read certain objects — and the user grants per app. Network in particular should be off unless asked for: an app that can’t make outbound requests can’t exfiltrate anything, which removes most of the damage even if the code is hostile. Both of my apps run fully offline; that should be the default and probably covers the large majority of real use cases.

Tighter isolation of the execution context, so an app can’t reach the host application around it. I’d rather not detail that part in public, but I’m glad to share what I found with the team directly.

Visibility and a kill switch. A clear indicator that a page is running an app, and a per-space toggle to disable execution entirely.

Fully agree with kaye’s advice in the meantime: non-sensitive space, read the code, know what you’re running. Worth adding that mini apps are objects, so a malicious one spreads exactly the way a useful one does — that’s the same property, and it’s why the trust model has to come before any wider rollout rather than after.

Happy to help on any of this if it’s useful.