Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render to string to support server-side rendering (SSR) #657

Closed
MadLittleMods opened this issue Feb 3, 2022 · 5 comments
Closed

Render to string to support server-side rendering (SSR) #657

MadLittleMods opened this issue Feb 3, 2022 · 5 comments
Labels
feature New feature or request sdk

Comments

@MadLittleMods
Copy link
Contributor

MadLittleMods commented Feb 3, 2022

Is your feature request related to a problem? Please describe.

For feature parity with Gitter, we're developing a new Matrix public archive so all of the Gitter content on Matrix is still indexable by search engines (and bring this benefit to all of the other Matrix rooms).

The plan is to server-side render some Hydrogen components to get a native(to Element) feeling experience (and the maintenance of keeping up with different message types). Currently, Hydrogen uses a lot of DOM document API's to construct the views which aren't available in Node.js and makes it harder to server-side render.

Describe the solution you'd like

Add a way to render Hydrogen components to a string.

For reference, Vue.js renderer.renderToString

Describe alternatives you've considered

In Node.js land, we could instead maybe use the document API's from https://github.com/jsdom/jsdom but this feels heavy compared to how close render to string might be.

Other related libraries:

Additional context

Since Hydrogen has a lot of the DOM usage abstracted up to src/platform/web/ui/general/html.ts and src/platform/web/ui/general/TemplateView.ts, it seems possible to shove a new TemplateBuilder in place which renders to a string instead.

I've made a very rough proof of concept in #656 which kinda works but there are some obstacles:

Problems

  • Some render() functions have DOM side-effects
  • t.view(...) causes a lot of side-effects since it calls mount() (which has side-effects), then render()
    • In my use, the ListView which is an IView and does a bunch of mount document.appendChild stuff
    • A TemplateView is much more compatible since we can just render and get a string mostly

-- #656

In terms of actually doing it, I'm curious about your thoughts and taste-making here! 🙂

@MadLittleMods MadLittleMods added feature New feature or request sdk labels Feb 3, 2022
@bwindels
Copy link
Contributor

bwindels commented Feb 3, 2022

First thought is that not all views use TemplateView (ListView is a notable example) and the only abstraction that can be assumed throughout the app is IView which assumes the DOM. Perhaps using something like JSDOM would be easier?

@MadLittleMods
Copy link
Contributor Author

MadLittleMods commented Feb 3, 2022

(ListView is a notable example) and the only abstraction that can be assumed throughout the app is IView which assumes the DOM.

That's what I ran into as well but why does ListView have to be an IView? What's the goal of the difference?

It seems like they could be a TemplateView and there appears to be only a handful to switch over.

Side-effects which assume DOM could be moved into helpers like TemplateBuilder.mapSideEffect (or dedicated hooks) and just not run the side-effects when rendering to string. Maybe something like the useEffect in React.

Perhaps using something like JSDOM would be easier?

I can explore this route more, just feels like it's almost possible with the current code and PoC.

@MadLittleMods
Copy link
Contributor Author

MadLittleMods commented Feb 4, 2022

For reference, I was able to get basic SSR working with linkedom with a few edits to Hydrogen and seems pretty promising (see https://github.com/matrix-org/matrix-public-archive).

Still would be interesting to do without that extra faff and render to string directly. I'm curious about the questions in my previous reply but I know you may not want to push forward here.

@bwindels
Copy link
Contributor

(ListView is a notable example) and the only abstraction that can be assumed throughout the app is IView which assumes the DOM.

That's what I ran into as well but why does ListView have to be an IView? What's the goal of the difference?

I might be misunderstanding the question, but the goal is that all views share a common interface, so a TemplateView can be mounted inside a ListView and vice-versa, independent of how they are implemented. There's even StaticView that doesn't do data binding or event handlers fwiw. We also want to keep the possibility open to have an IView that wraps for example a React or Vue view if we want to integrate some third party library. The only common denominator is really the DOM.

It seems like they could be a TemplateView and there appears to be only a handful to switch over.

Side-effects which assume DOM could be moved into helpers like TemplateBuilder.mapSideEffect (or dedicated hooks) and just not run the side-effects when rendering to string. Maybe something like the useEffect in React.

Sorry, but I don't think we want to add this complication just so server-side rendering wouldn't need to use a DOM implementation.

Perhaps using something like JSDOM would be easier?

I can explore this route more, just feels like it's almost possible with the current code and PoC.

It's up to you, but I don't think we want to accommodate too much within the Hydrogen view layer to render with anything else than a DOM implementation. If you can make it work, great, but it's a fragile setup IMO. We could change where we get the DOM implementation from (e.g. allow to inject it rather than hardcode the document global variable, but things other than that should probably go outside of the SDK.

If you have problems with linkedom, I'd recommend to giving JSDOM a try.

@MadLittleMods
Copy link
Contributor Author

With the same Hydrogen ethos in mind (minimizing dependencies), it would be nice to do away with the DOM implementation for Node.js. At this point, we have a working enough example with linkedom so it's not a real blocker.

[...] I don't think we want to accommodate too much within the Hydrogen view layer to render with anything else than a DOM implementation.

The case against render to string mostly boils down to this which is fine ⏩ It's not too unreasonable.


I might be misunderstanding the question, but the goal is that all views share a common interface, so a TemplateView can be mounted inside a ListView and vice-versa, independent of how they are implemented.

The common interface could be TemplateView and ListView could use that as well.

We also want to keep the possibility open to have an IView that wraps for example a React or Vue view if we want to integrate some third party library. The only common denominator is really the DOM.

Both Vue and React have the possibilities to render to a node or a string so it would be compatible with a TemplateView only world.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request sdk
Projects
None yet
Development

No branches or pull requests

2 participants