skip to Main Content

👋 Hi devs!

Currently i am trying to create a template engine with only react and htm. It’s think out the box, so, no CRA, no Nextjs, no other framework or library. So, It’s an hard way.

I need to undestand why i can’t load the same librs from server-side-rendered into client-side-rendering.

NodeJs is setted in "type:module" and the ssr scripts is equal to that csr because it’s the pre-runned printed version of it.

I’m going crazy for run all libs both in nodejs and on the web page.

exemple:


import React from 'react'
import ReactDOM from 'react-dom/client'
import ReactDOMServer from 'react-dom/server'
import htm from 'htm'
const html = htm.bind(React.createElement)

// it run on node middleware, it doesn't run on web and result is:
// Uncaught TypeError: Failed to resolve module specifier "react". Relative references must start with either "/", "./", or "../"

The docs import it in the same mode as you see in my codes, why he can do it???

https://github.com/developit/htm
https://react.dev/learn/add-react-to-an-existing-project


Goal: Finding a solution or correct approach for load and parallize the libraries and set correctly react and htm between server side and client web side.

2

Answers


  1. Chosen as BEST ANSWER

    Solutions:

    First Basic Solution:

    Warning: THE APPROACH BELOW FOR MODULES CAN'T WORK! Some modules are plain scripts, some else are cjs, other umd... Its doesn't have a compatibility for taken this way.

    "set all in relative path after make static the modules"

    yourexpress.use( '/node_modules/', express.static( yourbaseroot+'/node_modules/') )
    
    import React from '../node_modules/react/index.js'
    import { hydrateRoot } from '../node_modules/react-dom/index.js'
    import { renderToString } from '../node_modules/react-dom/server.js'
    import { html } from '../node_modules/htm/react/index.js'
    




    Second (and better) Approach:

    One (or the only) of better solution it's make a cross load function. In my case i make an importer async make a first good solution.

    // importer:
    
    async function importModule(modulePath) {
        if(typeof window === 'undefined'){
            return require(modulePath)
        } else {
            const module = await import(modulePath)
            return module.default || module
        }
    }
    
    //usage:
    
    const MyReactComponent = async (props) => {
    
        var React, MyLib
    
        if(typeof window === 'undefined'){
    
            React = await importModule('react')
            MyLib = await importModule('MyLib')
    
        } else {
    
            React = window.React 
            MyLib = window.MyLib
    
        }
    
        // do...
    
    }
    
    

    in any case, however, you need to use the horrible solution of export into window global object part of your app (like a builder, ex webpack)




    Other notes, approach and deliriums

    After different try I understand that right way is respect imports/exports types and not load the libs direct via nodejs modules paths in search of an unpraticable absolutism. However, commonJS and ES6 are an unconciliable enemies if you have its into the same page and you think to share something. One lock the others and vice versa in recursive loop.

    So, it's a good idea to understand (obviously) the file structure of what we want to do, set up node for commonJS and get/share to parallelize your libraries both on the server and the web window object.

    function myLibOrComponent () { ... }
    
    typeof window === 'undefined' ? 
        module.exports = myLibOrComponent :        // node export
        window.myLibOrComponent = myLibOrComponent // web export
    

    so, get it in same way... exemple:

    const myLibOrComponent = typeof window === 'undefined' ?
        require('myLibOrComponent ') :  // node import 
        window.myLibOrComponent         // web global object
    

    another way for components is:

    import('./myLibOrComponent.js').then( () => { do... })
    const myLibOrComponent = React.lazy(() => import('./myLibOrComponent'))
    

    Exporting in window or self (like webpack) global object it's not a good practice but, unfortunately, I don't find any other way to reconcile the two type of script, formatted for two (or more) type of importing type.


  2. Your browser can’t import from react. Browsers (like ESM Node.js) use the same syntax for imports, but there’s one really large difference: Node knows about node_modules, but browsers do not.

    The typical solution for this is indeed a bundler like webpack, vite, etc.

    If you’re looking for a way to build universal code without a bundler, it’s going to be very hard if you also want to use existing packages. You may be able to do something with import maps but if you’re completely trying to avoid a build step, being able to use import maps is going to depend on how these specific packages are built. It will for sure be a challenge (but perhaps not impossible).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search