Kent C. Dodds
Your brain needs this ðŸ§
What changed?
if (navigator.geolocation) {
  // use geolocation API
}Basic content should be accessible to all web browsers.
Basic functionality should be accessible to all web browsers.
Sparse, semantic markup contains all content.
Enhanced layout is provided by externally linked CSS.
Enhanced behavior is provided by externally linked JavaScript.
End-user web browser preferences are respected.
without JS???
<form method="post" action="/todos" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="todoId" value="cl7yrv54a0045j1dz7etu1u9z" />
  <input type="hidden" name="complete" value="true" />
  <button
    type="submit"
    name="intent"
    value="toggleTodo"
    class="toggle"
    title="Mark as complete"
  >
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="40"
      height="40"
      viewBox="-3 -3 105 105"
    >
      <circle
        cx="50"
        cy="50"
        r="50"
        fill="none"
        stroke="#ededed"
        stroke-width="3"
      ></circle>
    </svg>
  </button>
</form>
What changed?
Dev Experience
User Experience
Even if you are building Figma!
Even if you are building Figma!
function ListItem({ todo, filter }: { todo: TodoItem; filter: Filter }) {
  const updateFetcher = useFetcher();
  const toggleFetcher = useFetcher();
  const deleteFetcher = useFetcher();
  const updateFormRef = React.useRef<HTMLFormElement>(null);
  const complete = todo.complete;
  const shouldRender =
    filter === "all" ||
    (filter === "complete" && complete) ||
    (filter === "active" && !complete);
  if (!shouldRender) return null;
  return (
    <li className={complete ? "completed" : ""}>
      <div className="view">
        <toggleFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <input type="hidden" name="complete" value={(!complete).toString()} />
          <button
            type="submit"
            name="intent"
            value="toggleTodo"
            className="toggle"
            title={complete ? "Mark as incomplete" : "Mark as complete"}
          >
            {complete ? <CompleteIcon /> : <IncompleteIcon />}
          </button>
        </toggleFetcher.Form>
        <updateFetcher.Form
          method="post"
          className="update-form"
          ref={updateFormRef}
        >
          <input type="hidden" name="intent" value="updateTodo" />
          <input type="hidden" name="todoId" value={todo.id} />
          <input
            name="title"
            className="edit-input"
            defaultValue={todo.title}
            onBlur={(e) => {
              if (todo.title !== e.currentTarget.value) {
                updateFetcher.submit(e.currentTarget.form);
              }
            }}
            aria-invalid={updateFetcher.data?.error ? true : undefined}
            aria-describedby={`todo-update-error-${todo.id}`}
          />
          {updateFetcher.data?.error && updateFetcher.state !== "submitting" ? (
            <div
              className="error todo-update-error"
              id={`todo-update-error-${todo.id}`}
            >
              {updateFetcher.data?.error}
            </div>
          ) : null}
        </updateFetcher.Form>
        <deleteFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <button
            className="destroy"
            title="Delete todo"
            type="submit"
            name="intent"
            value="deleteTodo"
          />
        </deleteFetcher.Form>
      </div>
    </li>
  );
}function ListItem({ todo, filter }: { todo: TodoItem; filter: Filter }) {
  const updateFetcher = useFetcher();
  const toggleFetcher = useFetcher();
  const deleteFetcher = useFetcher();
  const updateFormRef = React.useRef<HTMLFormElement>(null);
  const isToggling = Boolean(toggleFetcher.submission);
  const complete = todo.complete;
  const shouldRender =
    filter === "all" ||
    (filter === "complete" && complete) ||
    (filter === "active" && !complete);
  if (!shouldRender) return null;
  return (
    <li className={complete ? "completed" : ""}>
      <div className="view">
        <toggleFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <input type="hidden" name="complete" value={(!complete).toString()} />
          <button
            type="submit"
            name="intent"
            value="toggleTodo"
            className="toggle"
            title={complete ? "Mark as incomplete" : "Mark as complete"}
            disabled={isToggling}
          >
            {complete ? <CompleteIcon /> : <IncompleteIcon />}
          </button>
        </toggleFetcher.Form>
        <updateFetcher.Form
          method="post"
          className="update-form"
          ref={updateFormRef}
        >
          <input type="hidden" name="intent" value="updateTodo" />
          <input type="hidden" name="todoId" value={todo.id} />
          <input
            name="title"
            className="edit-input"
            defaultValue={todo.title}
            onBlur={(e) => {
              if (todo.title !== e.currentTarget.value) {
                updateFetcher.submit(e.currentTarget.form);
              }
            }}
            aria-invalid={updateFetcher.data?.error ? true : undefined}
            aria-describedby={`todo-update-error-${todo.id}`}
            disabled={isToggling}
          />
          {updateFetcher.data?.error && updateFetcher.state !== "submitting" ? (
            <div
              className="error todo-update-error"
              id={`todo-update-error-${todo.id}`}
            >
              {updateFetcher.data?.error}
            </div>
          ) : null}
        </updateFetcher.Form>
        <deleteFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <button
            className="destroy"
            title="Delete todo"
            type="submit"
            name="intent"
            value="deleteTodo"
            disabled={isToggling}
          />
        </deleteFetcher.Form>
      </div>
    </li>
  );
}SPA capabilities + MPA mental model
function ListItem({ todo, filter }: { todo: TodoItem; filter: Filter }) {
  const updateFetcher = useFetcher();
  const toggleFetcher = useFetcher();
  const deleteFetcher = useFetcher();
  const updateFormRef = React.useRef<HTMLFormElement>(null);
  const isToggling = Boolean(toggleFetcher.submission);
  const complete = todo.complete;
  const shouldRender =
    filter === "all" ||
    (filter === "complete" && complete) ||
    (filter === "active" && !complete);
  if (!shouldRender) return null;
  return (
    <li className={complete ? "completed" : ""}>
      <div className="view">
        <toggleFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <input type="hidden" name="complete" value={(!complete).toString()} />
          <button
            type="submit"
            name="intent"
            value="toggleTodo"
            className="toggle"
            title={complete ? "Mark as incomplete" : "Mark as complete"}
            disabled={isToggling}
          >
            {complete ? <CompleteIcon /> : <IncompleteIcon />}
          </button>
        </toggleFetcher.Form>
        <updateFetcher.Form
          method="post"
          className="update-form"
          ref={updateFormRef}
        >
          <input type="hidden" name="intent" value="updateTodo" />
          <input type="hidden" name="todoId" value={todo.id} />
          <input
            name="title"
            className="edit-input"
            defaultValue={todo.title}
            onBlur={(e) => {
              if (todo.title !== e.currentTarget.value) {
                updateFetcher.submit(e.currentTarget.form);
              }
            }}
            aria-invalid={updateFetcher.data?.error ? true : undefined}
            aria-describedby={`todo-update-error-${todo.id}`}
            disabled={isToggling}
          />
          {updateFetcher.data?.error && updateFetcher.state !== "submitting" ? (
            <div
              className="error todo-update-error"
              id={`todo-update-error-${todo.id}`}
            >
              {updateFetcher.data?.error}
            </div>
          ) : null}
        </updateFetcher.Form>
        <deleteFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <button
            className="destroy"
            title="Delete todo"
            type="submit"
            name="intent"
            value="deleteTodo"
            disabled={isToggling}
          />
        </deleteFetcher.Form>
      </div>
    </li>
  );
}function ListItem({ todo, filter }: { todo: TodoItem; filter: Filter }) {
  const updateFetcher = useFetcher();
  const toggleFetcher = useFetcher();
  const deleteFetcher = useFetcher();
  const updateFormRef = React.useRef<HTMLFormElement>(null);
  const isToggling = Boolean(toggleFetcher.submission);
  const complete = isToggling
    ? toggleFetcher.submission?.formData.get("complete") === "true"
    : todo.complete;
  const shouldRender =
    filter === "all" ||
    (filter === "complete" && complete) ||
    (filter === "active" && !complete);
  if (!shouldRender) return null;
  return (
    <li className={complete ? "completed" : ""}>
      <div className="view">
        <toggleFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <input type="hidden" name="complete" value={(!complete).toString()} />
          <button
            type="submit"
            name="intent"
            value="toggleTodo"
            className="toggle"
            title={complete ? "Mark as incomplete" : "Mark as complete"}
          >
            {complete ? <CompleteIcon /> : <IncompleteIcon />}
          </button>
        </toggleFetcher.Form>
        <updateFetcher.Form
          method="post"
          className="update-form"
          ref={updateFormRef}
        >
          <input type="hidden" name="intent" value="updateTodo" />
          <input type="hidden" name="todoId" value={todo.id} />
          <input
            name="title"
            className="edit-input"
            defaultValue={todo.title}
            onBlur={(e) => {
              if (todo.title !== e.currentTarget.value) {
                updateFetcher.submit(e.currentTarget.form);
              }
            }}
            aria-invalid={updateFetcher.data?.error ? true : undefined}
            aria-describedby={`todo-update-error-${todo.id}`}
          />
          {updateFetcher.data?.error && updateFetcher.state !== "submitting" ? (
            <div
              className="error todo-update-error"
              id={`todo-update-error-${todo.id}`}
            >
              {updateFetcher.data?.error}
            </div>
          ) : null}
        </updateFetcher.Form>
        <deleteFetcher.Form method="post">
          <input type="hidden" name="todoId" value={todo.id} />
          <button
            className="destroy"
            title="Delete todo"
            type="submit"
            name="intent"
            value="deleteTodo"
          />
        </deleteFetcher.Form>
      </div>
    </li>
  );
}switch (intent) {
  case "toggleTodo": {
    await prisma.todo.update({
      where: { id: todoId },
      data: { complete: formData.get("complete") === "true" },
    });
    return new Response("ok");
  }
  // ...
}switch (intent) {
  case "toggleTodo": {
    return json({ error: "Random failure" }, { status: 500 });
    await prisma.todo.update({
      where: { id: todoId },
      data: { complete: formData.get("complete") === "true" },
    });
    return new Response("ok");
  }
  // ...
}