FIELD NOTE·29 APR 2026·5 MIN READ

Anativeabstractionlayerthatdoesn'tlietotheweb

Wrap Capacitor in capability hooks, not capability checks — and let the hooks lie to web on your behalf.

#engineering#mobile#capacitor#architecture

How ClearedMind keeps one React codebase honest across iOS, Android, and the browser without scattering platform checks.

The trap

The default Capacitor pattern looks like this:

if (Capacitor.isNativePlatform()) {
  await Haptics.impact({ style: ImpactStyle.Light });
}

Repeat that across a hundred components and you've effectively forked the codebase by attrition.

The pattern

In ClearedMind, no feature code ever calls Capacitor.isNativePlatform(). Instead, every native capability is wrapped in a hook:

export function useHaptics() {
  return {
    impact: async (style: 'light' | 'medium' | 'heavy') => {
      if (!Capacitor.isNativePlatform()) return;
      await Haptics.impact({ style: mapStyle(style) });
    },
  };
}

A button just calls haptics.impact('light'). The hook decides whether that becomes a real Taptic Engine pulse, an Android vibration, or nothing at all on web.

Why it matters

  • Feature code stays platform-agnostic.
  • Web stays a first-class target.
  • Refactors are cheap. When Capacitor 9 reshapes the Haptics API, exactly one file changes.

The rule

If you're typing isNativePlatform in feature code, you're missing a hook.