LumenJS deep dive — Socket

LumenJS deep dive — Socket

This article is part of the LumenJS deep dive series a series about the architectural decisions behind LumenJS, an open-source full-stack web framework designed for agent-driven development.

subscribe() solved one direction. Server pushes, client listens. But some features need both sides talking. Chat, collaborative editing, presence, games. For that we added socket().

Socket

Same pattern, different primitive. Each page can export a socket() function. It runs on the server, sets up event listeners, and gives you a two-way channel — the client can send, the server can respond or broadcast.

Article content

The server listens with on(). It pushes back with push(). It broadcasts to a room with room.broadcast(). The cleanup function runs when the client disconnects.

On the component side, the framework injects an emit() method automatically, no wiring, no import.

Article content

That's the full loop. Client emits, server receives, server broadcasts, components update.

Article content

Rooms

Rooms are first-class. Join on connect, leave on disconnect, broadcast to everyone or everyone except the sender.

Article content

broadcast() excludes the sender. broadcastAll() includes everyone. Pick what fits.

When to use subscribe vs socket

The decision is simple. If the data only flows one way, server to client, use subscribe(). It uses native SSE, zero dependencies. If the client needs to send events too, use socket(). It uses Socket.IO, installed separately with lumenjs add socketio.

No overhead either way. Socket.IO client is only loaded on pages that export a socket() function.

When socket grows

Same rule as loaders and subscribe. Move the handler out to _socket.ts in the page folder. LumenJS picks it up automatically.

Article content

Conclusion

Three primitives. loader() for initial data. subscribe() for server push. socket() for bidirectional communication. Each one follows the same convention, a named export, collocated with the component, stripped from the client bundle.

The tradeoff here is infrastructure. Socket.IO needs a stateful server with sticky sessions. Traditional serverless functions don't fit. Plan for it before you reach for it.

To view or add a comment, sign in

More articles by Aymen LABIDI

  • LumenJS deep dive — llms.txt

    This article is part of the LumenJS deep dive series — a series about the architectural decisions behind LumenJS, an…

  • Claude Code is Awesome. Still Not Enough.

    In the last two years I've generated thousands, probably millions of lines of code. Long-running agents.

  • Agentic DevTeam @Nuraly

    One of the most challenging parts of the software development cycle is maintenance. Over time, teams face turnover…

    1 Comment
  • LumenJS deep dive — Subscribe

    One of the decisions you make early when building a framework is how to handle real-time data. Most solutions reach for…

  • LumenJS deep dive — Loaders

    One of the best things about building a framework is that you get to fix what bothers you in others, and simplify what…

  • LumenJS - A framework designed for coding agents

    Most frameworks are designed for developers. LumenJS is designed for coding agents and the developers who work with…

    3 Comments
  • Why Building Agents Is an Operations Problem, Not a Technology One

    Adding a chatbot UI to an LLM is not enough anymore. And if we're being honest with ourselves, it was never enough.

  • Nuraly | How We Ship Code with AI Agents

    We've been heavily using code agents especially Claude Code in our development cycle at Nuraly. But not alone.

  • I Waited. Here's What I Think About OpenClaw.

    I deliberately waited before saying anything about OpenClaw. When something gets this much hype, I've learned to let…

  • How Key-Value service can enhance platform UX

    When you're building a platform, you're not just developing for customers. You're developing for yourself first.

Others also viewed

Explore content categories