DEV Community

Rails Designer
Rails Designer

Posted on • Originally published at railsdesigner.com

Introducing Turbo Transition: create smoother Turbo Streams

This article was originally published on Rails Designer


Ever wondered how to add more joy to components and partials injected or removed from the DOM? Something like this:

Image description
(see original article for the actual gif)

Previously I used a solution that relied on event callbacks from turbo. It did its job, but I was never really happy with the solution nor with its usage.

So I am introducing: Turbo Transition: A β€œminion” for Turbo-Frames and Streams that transitions elements as they enter or leave the DOM. ✨ A way simpler, but equally powerful solution than what I had before.

Since I've been working actively on Rails Designers I have been exploring all kinds of interesting techniques. Turbo Transition, just like turbo-frame and turbo-stream is nothing more than a custom element that adds and removes defined CSS classes.

Check it out for your nextcurrent Turbo-powered app.

Interested how Turbo Transition and custom elements work? Continue reading. πŸ‘‡

How Turbo Transitions works

Turbo Transition is nothing more than a custom element. They let you create your own HTML tags with built-in behavior (like extending HTML's vocabulary). Here's the minimal setup:

class MyElement extends HTMLElement {
  // Called when element is added to the page
  connectedCallback() {
    // Element is now in the DOM
  }

  // Called when element is removed
  disconnectedCallback() {
    // Clean up time
  }
}

// Register so browsers know about the new element
customElements.define("my-element", MyElement);
Enter fullscreen mode Exit fullscreen mode

Turbo Transition builds on this foundation to handle animations when elements enter or leave the page:

class TurboTransition extends HTMLElement {
  connectedCallback() {
    // Handle enter animations
  }

  remove() {
    // Handle leave animations
  }
}

customElements.define("turbo-transition", TurboTransition);
Enter fullscreen mode Exit fullscreen mode

From here, it manages transition classes, timing, and cleanup.

class TurboTransition extends HTMLElement {
  connectedCallback() {
    if (this.#config.hasEnterTransition()) {
      this.#enter();
    }
  }

  remove() {
    if (this.#config.hasLeaveTransition()) {
      this.#leave();

      return this;
    }

    return super.remove();
  }
}
Enter fullscreen mode Exit fullscreen mode

The transition process is straightforward:

async #transition({ to, element = null }) {
  const target = element || this.firstElementChild;

  if (!target) return;

  const classes = this.#config.getClasses({ for: to });

  await this.#utilities.run(target, classes);
}
Enter fullscreen mode Exit fullscreen mode
  1. Get the target element (Turbo Transition requires one child element inside it);
  2. Fetch the relevant classes from attributes (eg. enter-from-class="fade-enter-from");
  3. Run the transition sequence.

The remove() method uses cloning to keep the animation visible while the original element is removed:

#leave() {
  const clone = this.cloneNode(true);
  const parent = this.parentNode;

  parent.replaceChild(clone, this);

  // Run transition on the clone's content
  this.#transition({ to: "leave", element: clone.firstElementChild })
    .then(() => clone.parentNode?.removeChild(clone));
}
Enter fullscreen mode Exit fullscreen mode

Classes are applied in a specific sequence to create smooth transitions:

// utilities.js
async run(element, classes) {
  this.#applyInitialState(element, classes); // Add 'from' + 'active'

  await this.#nextFrame(); // Wait for browser

  this.#applyFinalState(element, classes); // Switch to 'to' state

  await this.#waitForCompletion(element); // Wait for animation

  this.#cleanup(element, classes); // Remove classes
}
Enter fullscreen mode Exit fullscreen mode

This creates a reliable way to add smooth transitions to your components and elements that works seamlessly with Turbo Frames and Turbo Streams.

Check it out and give it that star! ⭐

Top comments (0)