DEV Community

Cover image for How I Built a Millisecond-Fast Squaredle Solver with Astro and React Islands
Dearest
Dearest Subscriber

Posted on

How I Built a Millisecond-Fast Squaredle Solver with Astro and React Islands

Hey DEV community!

For the last few months, I've been working on a passion project that I'm incredibly excited to finally share with you all. It started, as many projects do, from a personal frustration. I'm a big fan of the word puzzle game Squaredle, but I found the existing online solvers to be... lacking.

I was stuck in a frustrating dilemma:

Option A: A solver with an accurate, official dictionary, but a clunky, ad-filled UI that felt like it was designed in 2010 and was barely usable on mobile.

Option B: A solver with a modern, slick interface, but an unofficial dictionary that frequently gave wrong answers or missed key words.

I thought to myself: "Why can't we have both? Professional accuracy AND a delightful user experience?"

So, I decided to build it myself. Today, I'm launching SquaredleSolver.org, and I wanted to share the technical journey of how I built it, particularly my choice of architecture.

Screenshots cannot display the visualized path. Please visit the website for the actual experience. https://squaredlesolver.org/

The Goal: Performance First, Experience Always
My primary goal was to create a solver that felt instantaneous. Word puzzles are about quick thinking, and the tool should match that pace. This meant I needed:

A lightning-fast initial page load (First Contentful Paint).

Highly interactive components that load only when needed.

A non-blocking UI, especially during the heavy computation of solving the puzzle.

A modern, clean, and mobile-first interface.

This led me down a path of exploring different frameworks. While a standard React/Next.js app could work, I was concerned about the initial JavaScript payload. For a tool that needs to be fast from the get-go, sending a large bundle of JS upfront felt like the wrong approach.

This is where Astro came in.

The Architecture: Astro + React Islands 🏝️
For those unfamiliar, Astro is a web framework designed for building content-rich, performance-first websites. Its killer feature is zero-JS-by-default. It renders your entire site to static HTML, and only ships JavaScript for the interactive components you explicitly specify.

This concept of "islands of interactivity" was the perfect model for my solver.

Here's how my page is structured:

<Layout>
  <header>...</header>
  <main>
    <div class="prose">...</div>
  </main>

  <GridInput client:load />
  <ResultsPanel client:visible />
</Layout>
Enter fullscreen mode Exit fullscreen mode

My page is mostly static content—the header, the instructions, the FAQ. This is pure HTML and CSS, which loads instantly. The interactive parts, like the grid input and the results panel, are React components treated as "islands."

I used different client:* directives to control how these islands load:

: The grid input is the main interactive element, so I load its JavaScript immediately after the page loads.

: The results panel only needs to be interactive when the user scrolls down to see it. This directive lazy-loads the component's script until it enters the viewport.

This architecture gave me the best of both worlds: the raw speed of a static site generator and the powerful UI capabilities of React, precisely where I needed them.

Core Feature: A Non-Blocking Solver Engine
The most computationally intensive part of the app is solving the grid. It involves traversing a Trie data structure with over 195,000 words and running a recursive search (DFS) across the grid.

If I ran this on the main thread, the UI would freeze for a couple of seconds, creating a terrible user experience.

The solution? Web Workers.

When the user clicks "Solve," I spin up a new Web Worker to handle the entire process in the background:

Main Thread: Packages the grid data and sends it to the worker.

Worker Thread:

Loads the pre-processed Trie dictionary.

Executes the findAllWords search algorithm.

Once complete, it posts the results (a list of found words) back to the main thread.

Main Thread: Receives the results and updates the React state in the ResultsPanel to display the words.

This ensures the UI remains completely responsive. The user can still scroll, click, and interact with the page while the puzzle is being solved.

Here's a simplified look at the logic:

TypeScript

// In my SolverEngine.tsx React component

const handleSolve = () => {
  setIsLoading(true);
  const worker = new Worker('/workers/solver.js');

  worker.postMessage({
    grid: currentGrid,
    dictionary: 'nwl2023'
  });

  worker.onmessage = (e) => {
    const { results } = e.data;
    setSolvedWords(results);
    setIsLoading(false);
    worker.terminate();
  };
};
Enter fullscreen mode Exit fullscreen mode

The Little Details: Styling and UX
For styling, I chose Tailwind CSS. Its utility-first approach allowed me to build a clean, custom design system quickly without writing a single line of custom CSS. The JIT (Just-in-Time) compiler ensures that only the CSS classes I actually use are included in the final stylesheet, keeping it incredibly small.

One of my favorite features is the path visualization. When you click a word in the results, it animates the path on the grid. This was a fun challenge involving:

Calculating the precise coordinates of each grid cell.

Dynamically generating an SVG overlay with smooth curves.

Using CSS animations to "draw" the path.

It turned a simple "cheat tool" into a learning companion.

Final Thoughts and What's Next
Building SquaredleSolver has been a fantastic learning experience. Choosing Astro was a game-changer for performance, and it forced me to think deliberately about which parts of my application truly needed to be interactive.

This project reinforced a key lesson for me: the architecture you choose should directly serve the user experience you want to create. For a tool that needed to be fast, simple, and powerful, the Astro + React Islands model was a perfect fit.

I've just launched it, and the journey is far from over. I'm already planning to add features like historical puzzle archives and user stats.

I would be incredibly grateful for any feedback from the DEV community. Please give it a try and let me know what you think!

Check it out here 👉 https://squaredlesolver.org/

Thanks for reading!

Top comments (0)