Modular Interaction Layer (MIL)

By Everett Quebral
Picture of the author
Published on

Decoupling Interaction from Implementation

Overview

The Modular Interaction Layer (MIL) introduces a powerful abstraction for defining, managing, and orchestrating user interactions in a completely decoupled and composable manner. It allows UI developers to describe how interfaces respond to user intent, without hardwiring interaction logic to specific components, views, or platforms.

MIL provides a declarative interaction protocol where individual units of user interaction (taps, inputs, selections, gestures, voice, etc.) are captured and processed by reusable, platform-agnostic interaction modules. These modules can be registered, composed, and reused across any frontend surface or rendering context.


Why MIL?

Traditionally, UI interaction is entangled with components:

  • Event handlers live next to JSX or HTML
  • Gesture logic is embedded in views
  • Component trees define interaction flows

This tight coupling introduces friction:

  • Harder to reuse logic across components or platforms
  • Difficult to test or trace
  • Rewriting the same logic for web, native, and embedded

MIL solves this by turning interactions into modular, testable units that exist alongside rendering—not inside it.


Core Principles

  1. Interaction as Modules
    Events like click, swipe, submit, select, voice command are mapped to standalone functions (or finite state machines) that handle behavior.

  2. Composition over Conditionals
    Complex behaviors are composed from smaller interactions instead of deeply nested handlers.

  3. Platform Abstraction
    Inputs and gestures are abstracted into canonical actions (onSelect, onTrigger, etc.), mapped differently for web, mobile, or AR/VR.

  4. Declarative Interaction Binding
    Components declare which interaction modules they want to use—not how they implement it.


Conceptual Architecture

[User Event]
[Input Adapter (web, mobile, voice)]
[Interaction Module (MIL)]
[Behavior Pipeline]
[Effect: State Change, Command, Animation, Navigation]

Code Example: Declarative Interaction Handling

Let’s say we want to handle a login form submission.

1. Create a Modular Interaction

// interactions/onLoginSubmit.js
export const onLoginSubmit = ({ emit, getState, run }) => async () => {
  const { username, password } = getState('form');

  emit('login:started');
  try {
    const result = await run('authService.login', { username, password });
    emit('login:success', result);
  } catch (error) {
    emit('login:failed', error);
  }
};

2. Declare Interaction in Component

// LoginForm.jsx
import { useInteraction } from 'mil-react';
import { onLoginSubmit } from '../interactions/onLoginSubmit';

export function LoginForm() {
  const [form, setForm] = useState({ username: '', password: '' });

  const handleSubmit = useInteraction(onLoginSubmit, {
    getState: (key) => key === 'form' ? form : undefined,
    run: async (service, data) => {
      if (service === 'authService.login') {
        return fetch('/api/login', {
          method: 'POST',
          body: JSON.stringify(data),
          headers: { 'Content-Type': 'application/json' }
        }).then((res) => res.json());
      }
    },
    emit: (event, payload) => console.log(event, payload),
  });

  return (
    <form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
      <input
        type="text"
        value={form.username}
        onChange={(e) => setForm({ ...form, username: e.target.value })}
        placeholder="Username"
      />
      <input
        type="password"
        value={form.password}
        onChange={(e) => setForm({ ...form, password: e.target.value })}
        placeholder="Password"
      />
      <button type="submit">Login</button>
    </form>
  );
}

Input Adapters

Adapters translate native inputs into MIL interaction triggers:

Input SurfaceAdapter Behavior
Web BrowserMaps onClick, onChange, onSubmit to canonical events
MobileMaps touch, gesture, keyboard
Voice UIConverts spoken commands to triggers
Game/EmbeddedMaps controller or sensor input

Interaction Module Types

  • Trigger Module: Single action (e.g. button press)
  • Gesture Module: Multi-step input (e.g. drag, swipe)
  • FSM Module: Stateful flow (e.g. multi-step form)
  • Declarative Flow: DSL-like structure for describing complex behaviors

Use Cases for MIL

  • 🔁 Reusable Interactions across components, apps, platforms
  • 📱 Cross-Device Gesture Handling
  • 🎙️ Voice to UI Control Mapping
  • 🧪 Easy Unit Testing of interaction logic
  • 🧩 Composable UX Patterns (e.g. tooltips, modals, onboarding)
  • 🧠 Behavior-Driven Development (BDD) at the interaction level

Benefits of MIL

✅ Clear separation of interaction and UI
✅ Easier to test, compose, and reuse logic
✅ Platform-agnostic handling of input and feedback
✅ Reduces boilerplate in components
✅ Declarative and scalable interaction mapping


Integration with Other Layers

  • With CEL: MIL triggers composable execution flows
  • With APC: MIL drives dynamic presentation logic
  • With UIF: MIL maps user input into cross-channel intent
  • With CSEE: MIL interactions trigger surface-aware executions

Conclusion

The Modular Interaction Layer (MIL) redefines how we architect UI interactions—decoupling them from components, enabling reuse, and elevating them to first-class modular logic in your application stack.

It’s a key building block in composable systems, empowering teams to deliver scalable, maintainable, and cross-platform user experiences.

Stay Tuned

Want to become a Next.js pro?
The best articles, links and news related to web development delivered once a week to your inbox.