JSFiddle - React, Tailwind, and code Playground

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width" />

    <title>Boggle solver</title>

    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <div class="controls" tabindex="0">
      <form id="toolbar">
        <div>
          <div id="sizing">
            <label for="size">Number of cells/rows</label>
            <input
              type="number"
              class="tool"
              id="size"
              min="4"
              max="12"
              value="4"
            />
            <button type="submit" id="resize">Resize</button>
          </div>
        </div>
      </form>
      <form id="form">
        <table id="table">
          <tr>
            <td>
              <input class="input" value="" required />
            </td>
            <td>
              <input class="input" value="" required />
            </td>
          </tr>
          <tr>
            <td>
              <input class="input" value="" required />
            </td>
            <td>
              <input class="input" value="" required />
            </td>
          </tr>
        </table>
        <button type="submit" id="solve">Solve</button>
      </form>
      <div id="result"></div>
      <div id="anim"></div>
    </div>
  </body>
</html>

CSS

.input {
  text-transform: uppercase;
}

#result {
  word-break: break-word;
}

.blue {
  background-color: blue;
}

.red {
  background-color: red;
}

JavaScript

// a dumb dictionary sample, replace with actual words
const list = [
  "a",
  "e",
  "i",
  "o",
  "u",
];

class Node {
  constructor(key) {
    this.key = key;
    this.children = {};
    this.end = false;
  }
}

class Trie {
  constructor() {
    this.root = new Node(null);
  }
  insert(word) {
    if (!word.trim()) return undefined;
    let current = this.root;
    for (let i = 0; i < word.length; i++) {
      if (!current.children[word[i]]) {
        current.children[word[i]] = new Node(word[i]);
      }

      current = current.children[word[i]];

      if (i === word.length - 1) {
        current.end = true;
      }
    }
  }
  contains(word = "") {
    let current = this.root;
    for (let i = 0; i < word.length; i++) {
      if (!current.children[word[i]]) {
        return false;
      }
      current = current.children[word[i]];
    }
    return current.end;
  }
  find(prefix = "") {
    let current = this.root;
    for (let i = 0; i < prefix.length; i++) {
      if (!current.children[prefix[i]]) {
        return [];
      }
      current = current.children[prefix[i]];
    }
    const iterate = (prefix, node, words) => {
      const values = Object.values(node.children);
      for (let value of values) {
        if (value.end) words.push(prefix + value.key);
        iterate(prefix + value.key, value, words);
      }

      return words;
    };
    return iterate(prefix, current, [...(current.end ? [prefix] : [])]);
  }
}

const trie = new Trie();

for (let item of list) {
  trie.insert(item);
}

const movements = (i, j) => [
  { row: i, column: j + 1, move: "RIGHT" },
  { row: i + 1, column: j + 1, move: "BOTTOM_RIGHT" },
  { row: i + 1, column: j, move: "BOTTOM" },
  { row: i + 1, column: j - 1, move: "BOTTOM_LEFT" },
  { row: i, column: j - 1, move: "LEFT" },
  { row: i - 1, column: j - 1, move: "TOP_LEFT" },
  { row: i - 1, column: j, move: "TOP" },
  { row: i - 1, column: j + 1, move: "TOP_RIGHT" },
];
const renderState = (anim,i,j,move) =>...