Meteor + SolidJS demo

I started working on a (currently very simple) demo of using SolidJS as an alternative rendering engine for Meteor. Here’s the repo: GitHub - edemaine/solid-meteor-demo: Simple demo of SolidJS in Meteor

SolidJS was mentioned here previously but a quick summary: SolidJS code looks a lot like React, but builds fine-grained reactivity somewhat similar to Meteor’s Tracker, and runs component functions only once and reactively updates the template contents by treating {...} in JSX as reactive closures. The result is clean code (e.g. <div/> returns a DOM element — there is no virtual DOM) and extremely fast code. A lot of great ideas from React, Svelte, and Meteor.

The good news is that it’s relatively easy to enable SolidJS in Meteor, by modifying the Babel settings to use the SolidJS JSX compiler. There’s now also a plugin, edemaine:solid, to do this automatically and more carefully. It’s also really easy to combine Meteor reactivity and SolidJS reactivity, though it should get even easier with some helpers along the lines of react-meteor-data, which I’m thinking about writing and there’s now a library solid-meteor-data that makes this easy and efficient.

Some bad news (hopefully temporary):

  • To get SSR working, I think we need to modify the Meteor compiler to support different Babel options when compiling server vs. client-side code. If someone knows a way, let me know. Alternatively, it might be possible to make an alternative to the ecmascript plugin. SSR works now, thanks to edemaine:solid. See below.
  • I haven’t been able to get HMR working, I think because Meteor thinks the code looks like React so it’s automatically accepting the update; but I’m confused because all I get is “SSR connected” but no apparent updates. Basic HMR works now, thanks to Meteor 2.5.2! See below.
  • I can’t get the development mode of SolidJS to load (which offers some helpful checks/warnings). Related to this feature request.
14 Likes

An update on this: I made a library solid-meteor-data with helper functions similar to the react-meteor-data hooks, and now use them in the demo repo. Code is much cleaner now!

There are some things still to fix, though, as described in issues. In particular, the createSubscribe interface isn’t right (can’t be reactive as is); I could use feedback on the ideal interface here. createTracker, createSubscribe, and createFind should all be very reactive and functional now! It even has tests now.

1 Like

Thanks for sharing this. I’ve been wondering what would be involved in using solidjs with Meteor.

Build plugins that use the BabelCompiler package, such as ecmascript, are able to customize the babel config per file: meteor/plugin.js at 6a9d940ee6870e2fa85bbc0505d05b4a299d707b · meteor/meteor · GitHub.

Updating to Meteor 2.5.2 should fix HMR (the changes still won’t be applied with HMR, but it should now correctly fall back to hot code push instead of doing nothing).

1 Like

Thanks for the pointers! I haven’t explored SSR further yet, but I just confirmed that Meteor 2.5.3 indeed fixes HMR, and I could get actual HMR working (still completely rerendering, but at least not reloading).

I ran into this issue which might interest you, given that you made hot-module-replacement. :slightly_smiling_face:

I agree. The issue is that the same file may be used on both the client and server, and we need to use different Babel transforms in each case. Where that modifyBabelConfig function is called, the architecture arch (e.g. "web.browser") isn’t passed in, and nor is the closely related features object. A simple PR could pass it in, though. It’s possible the architecture could be inferred from babelOptions which is passed in…

Update: It turns out arch is embedded within babelOptions (the first argument). specifically as babelOptions.caller.arch. So it is possible! I’m working on it…

I built a Meteor package edemaine:solid that jumps through the compiler hoops to get SolidJS’s Babel configuration set up right, including with SSR which requires different settings on client and server. You can also specify a subset of files that use Solid, and the rest will use React (for transitioning).

The end result is that the demo now has SSR working. Hurray!

This would have been a lot easier if React wasn’t so embedded into Meteor, and if the there were a way to customize all BabelCompilers instead of having to replace all of ecmascript, typescript, and coffeescript. But at least it’s possible.

6 Likes

This is amazing! Thank you.

1 Like

I should probably also mention some significant improvements to solid-meteor-data over the past week:

  • Full integration! You can now turn on a global “auto” mode that makes SolidJS automatically react to all Meteor reactive data. No more createTracker/useTracker! You can just use e.g. <h1>Hello {Session.get('name')}</h1> and it will automatically react to the Session ReactiveDict. To my knowledge, no other system except Blaze supports this!
  • Fine-grained reactivity. createFind now returns a SolidJS Store to represent each document. So you can pass in a document doc to a component, and if that component uses e.g. {doc.title}, then only that part will update when doc.title changes, and nothing will update when other attributes of doc change.
  • Efficiency. createFind is extremely efficient now: multiple updates (e.g. after initial subscription) are batched together, and the array is modified in-place instead of being copied. This avoids a lot of quadratic-time behavior that is prevalent in react-meteor-data; solid-meteor-data should be much faster. And createFind should generally re-use components for the same document _id, like react-meteor-data.
  • SSR. I actually found a bug in react-meteor-data's useFind because my port had the same bug. Fixed in solid-meteor-data and reported to react-meteor-data. :slightly_smiling_face:
8 Likes

One more addition: edemaine:solid-template-helper allows you to embed SolidJS components within Blaze, just like react-template-helper. This makes it easier to transition from Blaze to Solid incrementally (as I need to do myself).

Given Solid’s first-class recognition of Meteor reactive data, just like Blaze, I think this transition is a natural one! As an exercise, here is the equivalent SolidJS code from a portion of Step 5 of the Blaze tutorial:

function mainContainer(props) {
  const tasks = createFind(() =>
    TasksCollection.find({}, { sort: { createdAt: -1 } }));
  return (
    <div class="app">
      <div class="main">
        <ul class="tasks">
          <For each={tasks()}>{task =>
            <Task task={task}/>
          }</For>
        </ul>
      </div>
    </div>
  );

function Task(props) {
  function toggleChecked() {
    TasksCollection.update(props.task._id, {
      $set: { isChecked: !props.task.isChecked },
    });
  }
  function delete() {
    TasksCollection.remove(props.task._id);
  }
  return (
    <li>
      <input type="checkbox" checked={props.task.isChecked} onClick={toggleChecked} />
      <span>{props.task.text}</span>
      <button onClick={delete}>&times;</button>
    </li>
  );
}

Here’s the simplified Step 5 Blaze code that I translated:

<template name="mainContainer">
  <div class="app">
    <div class="main">
      <ul class="tasks">
        {{#each tasks}}
          {{> task}}
        {{/each}}
      </ul>
    </div>
  </div>
</template>

<template name="task">
  <li>
    <input type="checkbox" checked="{{isChecked}}" class="toggle-checked" />
    <span>{{text}}</span>
    <button class="delete">&times;</button>
  </li>
</template>
Template.mainContainer.helpers({
  tasks() {
    return TasksCollection.find({}, { sort: { createdAt: -1 } });
  },
});

Template.task.events({
  'click .toggle-checked'() {
    TasksCollection.update(this._id, {
      $set: { isChecked: !this.isChecked },
    });
  },
  'click .delete'() {
    TasksCollection.remove(this._id);
  },
});

I find the notation quite close, and I like the ability to integrate the HTML and JavaScript together. And the meaning (in terms of reactivity) is quite similar, but even more fine-grained in Solid. For example, if a task’s text changes, then in Solid only that <span> will get updated, whereas in Blaze the helpers of the entire template will rerun to see if they changed.

2 Likes

Hi @edemaine, noticed you built the Meteor + Solid integration and curious what you think of this new Vite + Meteor integration :+1:

5 Likes