Invoking Solid.js components from your Ember apps
Tags: solidjs, ember, javascript, webdev
Published: 2023-02-13 06:56:59

In this post, we are going to take a look at how to invoke Solid.js components from an Ember.js application. Before diving into the topic let's have a quick intro about both these frameworks (or library) and look at why we want to integrate Solid.js into an Ember codebase first.

## Ember
[Ember](https://emberjs.com/) is a  framework for ambitious web developers. It is a productive, battle-tested JavaScript framework for building modern web applications. It includes everything you need to build rich UIs that work on any device. It has been around for more than 10 years and is still preferred and used by a lot of companies.

## SolidJS
[SolidJS](https://www.solidjs.com/) is a powerful, pragmatic and productive JavaScript library for building user interfaces with simple and performant reactivity. It stands on the shoulders of giants, particularly React and Knockout. If you've developed with React Functional Components and Hooks before, Solid will feel very natural because it follows the same philosophy as React, with unidirectional data flow, read/write segregation, and immutable interfaces.



## Why?
There could be many reasons to integrate SolidJS into an Ember.js application from performance, maintainability, technology heterogeneity, organizational reasons, developer availability to business priorities. My fellow Emberanos won't fully agree with me on these various reasons. It's okay. Yet I wanted to share my knowledge on some recent experiments I have done to help a larger community of people and to those who are looking to migrate from Ember to SolidJS.

It is important to remember that Ember is not a bad framework. And Ember is still my first love ❤️ when it comes to JavaScript Frameworks and I am very grateful to the framework and the community since it has been a tremendous help in shaping my front-end career and building a lot of [tools](https://github.com/rajasegar?tab=repositories&q=ember&type=&language=&sort=). You really can't compare Ember with SolidJS in absolute terms. Because it's apples and oranges.

## Setting up the Monorepo
We are going to setup a [monorepo](https://monorepo.tools/) for this post. That gives us a clear advantage of keeping the SolidJS components and the Ember app separately, still within a single repository. We are going to use [pnpm workspaces](https://pnpm.io/workspaces) for the task at hand.

```
|-app
|--|-<Ember app>
|-some-solid-lib
|--HelloWorld.jsx
|--package.json
|-README.md
|-pnpm-lock.yaml
|-pnpm-workspace.yaml
```

This is how I setup the monorepo structure using the command line.

```
mkdir ember-solid-example
cd ember-solid-example
touch README.md pnpm-workspace.yaml
mkdir app some-solid-lib
cd app
degit ember-cli/ember-new-output#v4.10.0
cd ..
cd some-solid-lib
touch HelloWorld.jsx package.json
```

Here I am using [degit](https://github.com/Rich-Harris/degit) to bootstrap our Ember app since the `ember-cli` doesn't allow you to create a new Ember app in the name of `app`.


Our `pnpm-workspace.yaml` file should look like something like this, indicating that the workspace contains two packages one Ember app and other SolidJS component library.

```yaml
packages:
  - app
  - some-solid-lib
```

## Compiling SolidJS components with Ember
Now we will see how to configure Ember build pipeline with `ember-auto-import` to tweak the Webpack builder underneath to compile SolidJS components. Before that we need to add the appropriate dependencies for compiling SolidJS components like `solid-js` and `solid-hot-loader`.

So from your Ember app root folder, run the following command to add the dependencies.

```
pnpm add -D solid-js solid-hot-loader babel-loader @babel/preset-env
```

This is how the modified `ember-cli-build.js` file inside the Ember app will look like. We are configuring the webpack loader to handle all the `.jsx` files from the SolidJS package `some-solid-lib` inside the monorepo.


```js

'use strict';

const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const path = require('path');

module.exports = function (defaults) {
  const app = new EmberApp(defaults, {
    autoImport: {
      webpack: {
        module: {
          rules: [
            {
              test: /\.jsx$/,
              use: {
                loader: 'babel-loader',
                options: {
                  babelrc: false,
                  configFile: false,
                  presets: ['@babel/preset-env', 'solid'],
                },
              },
            },
          ],
        },
      },
    },
  });

  return app.toTree();
};

```


## Rendering components via Modifiers

Modifiers are a basic primitive for interacting with the DOM in Ember. For example, Ember ships with a built-in modifier, `{{on}}`:

```hbs
<button {{on "click" @onClick}}>
  {{@text}}
</button>
```

All modifiers get applied to elements directly this way (if you see a similar value that isn't in an element, it is probably a helper instead), and they are passed the element when applying their effects.

Conceptually, modifiers take tracked, derived state, and turn it into some sort of side effect related in some way to the DOM element they are applied to.

To create our modifiers, we are going to use the `ember-modifier` addon inside our Ember app. Let's first install our addon.

```
ember install ember-modifier
```

Let's create a class-based modifier to render our SolidJS components.

```
ember g modifier solid --class
```

This is the code for the newly created modifier. Basically the modifier is trying to create a new Root element for the SolidJS component and then it creates a new instance of the SolidJS component and render it inside the element provided by the modifier. The `registerDestructor` function available in `@ember/destroyable` will help you to tear down the functionality added in the modifier, when the modifier is removed.

```js

import Modifier from 'ember-modifier';
import { render } from 'solid-js/web';
import { registerDestructor } from '@ember/destroyable';

export default class SolidModifier extends Modifier {
  modify(element, [App], props) {
    element.replaceChildren();
    const dispose = render(() => App(props), element);
    registerDestructor(this, dispose);
  }
}

```

## SolidJS Component
Our SolidJS component is a simple component showing a message that can be toggled using the actions of an Ember component.
This is the code for our SolidJS component.

```js
function HelloWorld({message, onClick}) {
  return (
	<div>
	  <button onClick={onClick}>Toggle</button>
	  <div>you said: {message}</div>
	</div>
	);
}

export default HelloWorld;

```


## Creating a Ember wrapper component
Let's create our wrapper component for which we need a Glimmer component with class

```
ember g component example -gc
```

### Ember component.js
The code for the Ember wrapper component is simple. First we are importing our SolidJS component from the shared library in our monorepo and assign it to a component property called `theSolidComponent` which we will be using the main argument to our modifier. Then we have a tracked property message and an action toggle which are both props to the SolidJS component. 

```js
import HelloWorld  from 'some-solid-lib/HelloWorld.jsx';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class extends Component {
  theSolidComponent = HelloWorld;

  @tracked message = 'hello';

  @action toggle() { 
    if (this.message === 'hello') {
      this.message = 'goodbye';
    } else {
      this.message = 'hello';
    }
  }
}
```

### Ember Component template
And this is how we use the `solid` modifier on a DOM element to render our SolidJS component and pass our data from Ember via the props.

```hbs
<div {{solid this.theSolidComponent message=this.message onClick=this.toggle}} />
```

And this is how the Ember app looks like. Basically we are toggling a message with a button. Both the message data and the message handler functions are passed from Ember to SolidJS component via the modifier props.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6lvegb5c9z2ciexuumgk.gif)

## Pros & Cons
Having used SolidJS components inside an Ember app, let's discuss about the pros and cons of using the above mentioned approach to integrate SolidJS into Ember.js applications.

### Pros
- Incrementally migrate an Ember codebase to SolidJS
- Having a monorepo of both Ember and SolidJS components
- Easy to consume the components, passing data via props and sharing the state with Modifier syntax
- Clean and simple approach

### Cons
- Need Ember wrapper components for each SolidJS Component
- Hot module reloading won't work if you change code in SolidJS components since it is a separate dependency via npm
- Passing children to SolidJS components is not possible, the SolidJS components need to have their own children

## Sample repo
The code for this post is hosted in Github [here](https://github.com/rajasegar/ember-solid-example). 

Please take a look at the Github repo and let me know your feedback, queries in the comments section. If you feel there are more pros and cons to be added here, please do let us know also.