Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Allow rendering in alternative (not browser) DOM implementation.

See original GitHub issue

I know that there is a preact-render-to -string project, but it have some limitations with accessing to real dom/document/window ect. from component, also async rendering is not trivial. There are few implementations of DOM for node.js eg domino. This will be very useful to have possibility to render directly to such implementations instead of render to string.

The required change is only to remove reference to global document object and replace it by using parent.ownerDocument. I saw there is only one on two places (createNode) in code which are using global document object. This should be very trivial work.

After such change there should be possibility to :

import * as domino from domino;
import { h, render} from 'preact';

var window = domino.createWindow();
var document = window.document;

    <div id="foo">
        <span>Hello, world!</span>
        <button onClick={ e => alert("hi!") }>Click Me</button>
), document.body);

Best Regards.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

majo44commented, Apr 19, 2017

Ok I found better workaround. I’m able to do this by the thread local storage (eg. Zone.js)

// exposing window from current zone
Object.defineProperty(global, 'window', {
    get: () => {
        return Zone.current.get('window');

// exposing document from current zone
Object.defineProperty(global, 'document', {
    get: () => {
        return Zone.current.get('document');

// this fn creates Zone, and expose the doc and win on it
function runInDomZone<T>(fn: () => T): T {
    let window = domino.createWindow(' ');
    let document: any = window.document;
    document.ownerDocument = document;
    return Zone.current.fork({
        name: 'DOMZone',
        properties: {
            window: window,
            document: document

// middleware 
// on express each request can go thought Zone 
app.use((req, res, next) => {

// req handler
app.get((req, res) => {
    // it is global document, but it is taken from Zone
    document.querySelector('header').append(.... // eg. some direct dom work
    fs.readFile( ....   //eg. some async work
    render(jsx, document.body);
    fs.readFile( ....   //eg.  some async work
    document.querySelector('header').append(.... // eg. some direct dom work
    res.send('<!DOCTYPE html>\n' + document.innerHTML);


developitcommented, Mar 1, 2017

I still don’t think this is something Preact will ever account for. It does not store or rely on any global state in the document, so there is absolutely no difference between passing render() a new document each time, or letting it share one. Furthermore, Preact (currently) recycles DOM elements - this would mean it is reusing DOM elements from one domino instance inside another domino instance - probably not something you want, and unlikely something domino properly accounts for (unless you’re calling document.importNode, but even then…

I don’t intend to pursue overriding the document global, and I think I’ve given fairly good justification for taking that position. Preact is a singleton by design, passing in a document reference doesn’t change anything about that. All it does is increase complexity in order to superficially support a use-case, without actually doing so. Think about the real implications of this: caching/recycling, creation, removal, etc - all of these must then take into account the fact that the document is not a global.

BTW - why do you need to use document.querySelector() in the first place? Just pass a new root element and query within that:

import domino from 'domino';
global.document = domino.createDocument();

function sendJSX(jsx, req, res) {
    // fresh DOM tree every time, with no locking issues:
    let root = document.createElement('root');

    root.querySelector('header').append(.... // eg. some direct dom work
    fs.readFile( ....   //eg. some async work
    render(jsx, root);
    fs.readFile( ....   //eg.  some async work
    root.querySelector('header').append(.... // eg. some direct dom work
    res.send('<!DOCTYPE html>\n' + root.innerHTML);
Read more comments on GitHub >

github_iconTop Results From Across the Web

Is a browser obliged to use a DOM to render an HTML page?
A Web browser is not obliged to use DOM in order to render an HTML document. You can find the entire context on...
Read more >
capricorn86/happy-dom - GitHub
The goal of Happy DOM is to emulate enough of a web browser to be useful for testing, scraping web sites and server-side...
Read more >
101 Javascript Critical Rendering Path -
First things first, every webpage has a Document Object Model or a DOM. This is an object-based representation of the entire HTML page,...
Read more >
[Proposal] (Another) Alternative to HTML - Architecture - WICG
Also, I am proposing an alternative to HTML, NOT a replacement. ... the browser attempts to use it's default facilities to render the...
Read more >
Declarative Shadow DOM -
Declarative Shadow DOM is a new way to implement and use Shadow DOM directly in HTML.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Post

No results found

github_iconTop Related Hashnode Post

No results found