DEV Community

Cover image for Adding "Suspense" with a Promise and a Mixin
Dario Mannu
Dario Mannu

Posted on • Edited on

Adding "Suspense" with a Promise and a Mixin

Did you ever feel that most JavaScript frameworks tend to overcomplicate things that should be trivial?

Take something as basic as showing a loading message until data arrives. In most frameworks, this turns into a full state management exercise with signals, stores, or hooks. But why should it be that hard?

Here’s a straightforward and elegant way to achieve this only with promises and attribute mixins.

We wanted a way to render a placeholder (like "loading…") first, and then replace it with the actual data once it’s ready. The catch is that Promises resolve only once, so you can’t easily express both states directly with one.

The solution? Use an Attribute Mixin — a simple object whose properties (like innerHTML, style, class, etc.) get merged into the element. Rimmel automatically handles that merge for you, whether the source is an object, a function, a Promise, or even an Observable.

Here’s how it looks:

import { rml } from 'rimmel';

const getData = () => fetch('/api')
  .then(r => r.text())
  .then(text => ({ innerHTML: text }))
;

document.body.innerHTML = rml`
  <div ...${getData()}>
    loading...
  </div>
`;
Enter fullscreen mode Exit fullscreen mode

And that’s it. If you expected anything longer, you may be disappointed now.
The <div> shows the placeholder immediately, and once the Promise resolves, Rimmel merges the resulting object into the element, replacing the text with the response.

Try it out yourself on StackBlitz or run the embedded demo below:


Why it works so well

Promises are a native, universally understood way to handle deferred values. By letting them flow directly into the DOM through Attribute Mixins, you skip any middle layer of framework-specific state handling. It’s clean, declarative, and entirely standard.


More about Attribute Mixins

An Attribute Mixin is just a plain object (or anything that resolves to one) whose properties are automatically merged into the target element. You can control attributes, styles, classes, text, or even children this way. They act as a simple, declarative replacement for directives — but composable and reactive by design.


If you liked this pattern, please consider leaving a GitHub ⭐ Star — it helps refining more stream-oriented patterns.


Learn More

Top comments (0)