If your application supports SSR - try to disable js and make it work without it. Then it would be clear what could be moved to a sidecar.

If your application is a client-side only SPA - try to imagine how it would work, if SSR existed.

For example - theurge.com, written in React, is fully functional without any js enabled.

There is a lot of things you may offload to a sidecar. For example:

comments. You might ship code to display comments, but not answer , as long as it might require more code(including WYSIWYG editor), which is not required initially. It's better to delay a commenting box, or even just hide code loading behind animation, than delay a whole page.

comments, but not , as long as it might require more code(including WYSIWYG editor), which is not required initially. It's better to delay a commenting box, or even just hide code loading behind animation, than delay a whole page. video player. Ship "video" without "controls". Load them a second later, them customer might try to interact with it.

image gallery, like slick . It's not a big deal to draw it, but much harder to animate and manage. It's clear what could be moved to a sidecar.

Just think what is essential for your application, and what is not quite...

Implementation details

(DI) Component code splitting

The simplest form of sidecar is easy to implement - just move everything to a sub component, you may code split using an "old" ways. It's almost a separation between Smart and Dumb components, but this time Smart is not contaniting a Dumb one - it's opposite.

const SmartComponent = React.lazy( () => import('./SmartComponent'));



class DumbComponent extends React.Component {

render() {

return (

<React.Fragment>

<SmartComponent ref={this} /> // <-- move smart one inside

<TheActualMarkup /> // <-- the "real" stuff is here

</React.Fragment>

}

}

That also requires moving initialization code to a Dumb one, but you are still able to code-split the heaviest part of a code.

Can you see a parallel or vertical code splitting pattern now?

useSidecar

Building the New facebook.com with React, GraphQL and Relay, I've already mentioned here, had a concept of loadAfter or importForInteractivity , which is quite alike sidecar concept.

In the same time, I would not recommend creating something like useSidecar as long you might intentionally try to use hooks inside, but code splitting in this form would break rule of hooks.

Please prefer a more declarative component way. And you might use hooks inside SideCar component.

const Controller = React.lazy( () => import('./Controller'));

const DumbComponent = () => {

const ref = useRef();

const state = useState();



return (

<>

<Controller componentRef={ref} state={state} />

<TheRealStuff ref={ref} state={state[0]} />

</>

)

}

Prefetching

Don't forget - you might use loading priority hinting to preload or prefetch sidecar and make it shipping more transparent and invisible.

Important stuff - prefetching scripts would load it via network, but not execute (and spend CPU) unless it actually required.

SSR

Unlike normal code splitting, no special action is required for SSR. Sidecar might not be a part of the SSR process and not required before hydration step. It's could be postponed "by design".

Thus - feel free to use React.lazy (ideally something without Suspense , you don't need any failback(loading) indicators here), or any other library, with, but better without SSR support to skip sidecar chunks during SSR process.

The bad parts

But there are a few bad parts of this idea

Batman is not a production name

While Batman / Robin might be a good mind concept, and sidecar is a perfect match for the technology itself - there is no "good" name for the maincar . There is no such thing as a maincar , and obviously Batman , Lonely Wolf , Solitude , Driver and Solo shall not be used to name a non-a-sidecar part.

Facebook have used display and interactivity , and that might be the best option for all of us.

If you have a good name for me - leave it in the comments

Tree shaking

It's more about the separation of concerns from bundler point of view. Let's imagine you have Batman and Robin . And stuff.js

export * from `./batman.js`

export * from `./robin.js`

Then you might try component based code splitting to implement a sidecar

//main.js

import {batman} from './stuff.js'



const Robin = React.lazy( () => import('./sidecar.js'));



export const Component = () => (

<>

<Robin /> // sidecar

<Batman /> // main content

</>

)



// and sidecar.js... that's another chunk as long as we `import` it

import {robin} from './stuff.js'

.....

In short - the code above would work, but will not do "the job".

if you are using only batman from stuff.js - tree shaking would keep only it.

from - tree shaking would keep only it. if you are using only robin from stuff.js - tree shaking would keep only it.

from - tree shaking would keep only it. but if you are using both, even in different chunks - both will be bundled in a first occurrence of stuff.js , ie the main bundle.

Tree shaking is not code-splitting friendly. You have to separate concerns by files.

Un-import

Another thing, forgotten by everybody, is the cost of javascript. It was quite common in the jQuery era, the era of jsonp payload to load the script(with json payload), get the payload, and remove the script.

Nowadays we all import script, and it will be forever imported, even if no longer needed.

As I said before - there is too much JS, and sooner or later, with continuous navigation you will load all of it. We should find a way to un-import no longer need chunk, clearing all internal caches and freeing memory to make web more reliable, and not to crush application with out of memory exceptions.

Probably the ability to un-import (webpack could do it) is one of the reasons we should stick with component-based API, as long as it gives us an ability to handle unmount .

So far - ESM modules standards have nothing about stuff like this - nor about cache control, nor about reversing import action.

Creating a sidecar-enabled Library

By today there is only one way to create a sidecar -enabled library:

split your component into parts

expose a main part and connected part(not to break API) via index

part and part(not to break API) via expose a sidecar via a separated entry point.

via a separated entry point. in the target code - import the main part and the sidecar - tree shaking should cut a connected part.

This time tree shaking should work properly, and the only problem - is how to name the main part.

//main.js

export const Main = ({sidecar, ...props}) => (

<div>

{sidecar}

....

</div>

);



// connected.js

import Main from './Component';

import Sidecar from './Sidecar';



export const Connected = props => (

<Main

sidecar={<Sidecar />}

{...props}

/>

);



//index.js

export * from './Main';

export * from './Connected';



//sidecar.js

import * from './Sidecar';



// -------------------------



//your app BEFORE

import {Connected} from 'library'; //



// -------------------------



//your app AFTER, compare to `connected.js`

import {Main} from 'library';

const Sidecar = React.lazy(import( () => import('library/sidecar')));

// ^ all the difference ^



export SideConnected = props => (

<Main

sidecar={<Sidecar />}

{...props}

/>

);



// ^ you will load only Main, Sidecar will arrive later.

Theoretically dynamic import could be used inside node_modules, making assemble process more transparent.

Anyway - it's nothing more than children / slot pattern, so common in React.

The future

Facebook proved that the idea is right. If you haven't seen that video - do it right now. I've just explained the same idea from a bit different angle (and started writing this article a week before F8 conference).

Right now it requires some code changes to be applied to your code base. It requires a more explicit separation of concerns to actually separate them, and let of codesplit not horizontally, but vertically, shipping lesser code for a bigger user experience.

Sidecar , probably, is the only way, except old school SSR, to handle BIG code bases. Last chance to ship a minimal amount of code, when you have a lot of it.

It could make a BIG application smaller, and a SMALL application even more smaller.

10 years ago the medium website was "ready" in 300ms, and was really ready a few milliseconds after. Today seconds and even more than 10 seconds are the common numbers. What a shame.

Let's take a pause, and think - how we could solve the problem, and make UX great again...

I love this image

Overall

1. Component code splitting is a most powerful tool, giving you the ability to completely split something, but it comes with a cost - you might not display anything except a blank page, or a skeleton for a while. That's a horizontal separation.

2. Library code splitting could help when component splitting would not. That's a horizontal separation.

3. Code, offloaded to a sidecar would complete the picture, and may let you provide a far better user experience. But would also require some engineering effort. That's a vertical separation.

Let's have a conversation about this.

Stop! So what about the problems you tried to solve?

Well, that was only the first part. We are in the endgame now, it would take a few more weeks to write down the second part of this proposal. Meanwhile...