Expo magic intensifies

Issue #345.November 26, 2024.2 Minute read.
Bytes

Today’s issue: Tailwind’s theme music, juicy rasterization secrets, and how to build Reddit without getting distracted by Reddit.

Welcome to #345.


Eyeballs logo

The Main Thing

David Blaine parody actor staring into the camera

Evan Bacon after every new Expo release

Expo magic intensifies

Just when you thought Expo was done blowing minds for a while, Evan Bacon and his band of merry magicians just dropped two major updates:

  1. RSC in Expo Router
  2. DOM Components

Let’s break both of those down and put them into context.

Magic #1: RSC in Expo Router: It’s still a highly experimental developer preview, but this is officially the first time you can use React server components and server functions in React Native 👏. There’s technically nothing stopping you from shipping it straight to prod – you can just do things.

Expo Router now lets you create a file with the "use server" directive, enabling you to export functions that return JSX. These are async-only functions that run on the server and require a network connection to resolve.

And unlike web frameworks like Next.js, Expo Router lets you use client components by default and mix in RSC where you need it. This means that your users still get instant interactivity and offline support, and it gives you fine-grained control of how the server is used.

Magic #2: DOM Components: Despite calling itself React Native, the cross-platform framework has never allowed you to easily convert a React website into a native app (go figure).

This is largely because React DOM elements like div and span weren’t available on native, because HTML, CSS, and browser APIs also weren’t available – until now.

Expo’s DOM components allow you to mark modules with a new "use dom" directive, import it into your native app, and have it run inside of a web view in your app.

There’s a lot of bundler magic going on behind the scenes to pull this off (when isn’t there nowadays?), but the end result is that you can run your React website as-is in your native app, then incrementally migrate it over to truly native views on a component-by-component basis.

Bottom Line: Expo’s pace of improvement keeps accelerating, and it’s nice to be along for the ride.

        

Inngest logo

Our Friends
(With Benefits)

Cuphead guy nervously smiling

When you’re 9 weeks into a 2-week AI agent sprint.

Inngest gives you *modern* queuing and orchestration

Most queuing solutions are a lot like my lower back: highly inflexible and likely to betray me when I need it most.

That’s why Inngest built a modern platform that gives you durable functions to replace queues, state management, and scheduling – without ever needing to touch infrastructure.

Turns out, this approach also works well for orchestrating agents in AI applications. And Inngest just launched two AI-native tools that make it even more powerful:

  • AgentKit TypeScript SDK helps you quickly build complex AI workflows that require multiple agents. It handles all the technical details around agent orchestration, state, and infra – so you can focus on just writing the AI code.

  • step.ai is a new set of core APIs that introduces features like request and response tracking, AI workflow tracing, and usage stats across AI model calls.

Try out the early access for free – and see how easy it is to just start building.


Pop Quiz logo

Pop Quiz

If you wanted to pause a CSS Keyframe animation using JavaScript, how would you do it?

<style>
  .circle {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-color: lightblue;
    margin: auto;
    animation: pulse 1s infinite ease-in-out;
  }

  @keyframes pulse {
    0% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.2);
    }
    100% {
      transform: scale(1);
    }
  }
</style>

<div class="circle"></div>
<button class="btn">Pause Animation</button>


Cool Bits logo

Cool Bits

  1. Wladislav Artsimovich wrote an in-depth, interactive article on Analytical Anti-Aliasing which includes “some juicy secrets” he’s never read anywhere else. And if that’s not enough to get you to read 5,000 words about image rasterization, I don’t know what will.

  2. Mastra is a TypeScript AI framework for prototyping and productionizing AI features with a modern JS/TS stack.

  3. Ian Vanagas wrote about the 5 principles PostHog uses to choose technologies. Surprisingly, one of these principles is not “blindly use whatever the meanest person on X says,” but to each their own. [sponsored]

  4. Webflow just launched the Webflow Design Language – a purely functional programming language that’s designed to be used visually.

  5. Reddit Staff Engineer, Jim Simon wrote about building Reddit’s frontend with Vite. It’s a great article, but he wrote it as a long Reddit post, so you’ll have to avoid getting distracted by all the outrage.

  6. Daishi Kato spoke about how an RSC framework enables Server Actions.

  7. TinyBase 5.4 introduces a new WebSocket synchronization server that runs on Cloudflare as a Durable Object.

  8. Tailwind just released their v4.0 Beta 1 with a unified toolchain, CSS-first configuration, and more. I knew I could hear Slayer playing somewhere in the distance.

  9. Brian Kardell wrote about interop and hard problems in the web platform.

  10. Cali is an AI agent that helps you build React Native apps, and as far as I can tell, it was not created by Evan Bacon.


Pop Quiz logo

Pop Quiz: Answer

You can use the element’s getAnimations method to get an array of all the animations on the element. From there you can loop through the array and call the pause() method on each animation.

const circle = document.querySelector(".circle");
const animations = circle.getAnimations();

animations.forEach(animation => {
  animation.pause();
})

Here’s what the full solution would look like, along with a working example if you’re the curious type.

<style>
  .circle {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-color: lightblue;
    margin: auto;
    animation: pulse 1s infinite ease-in-out;
  }

  @keyframes pulse {
    0% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.2);
    }
    100% {
      transform: scale(1);
    }
  }
</style>

<div class="circle"></div>
<button class="btn">Pause Animation</button>

<script>
  const btn = document.querySelector(".btn");
  
  btn.addEventListener("click", () => {
    const animations = document.querySelector(".circle")
      .getAnimations();
    
    if (btn.textContent === "Pause Animation") {
      animations.forEach((animation) => {
        animation.pause();
      });
      btn.textContent = "Play Animation";
    } else {
      animations.forEach((animation) => {
        animation.play();
      });
      btn.textContent = "Pause Animation";
    }
  });
</script>

There are other useful properties you can access on the animation object such as currentTime and playbackRate to have even more control over the animation.