Project Name | Stars | Downloads | Repos Using This | Packages Using This | Most Recent Commit | Total Releases | Latest Release | Open Issues | License | Language |
---|---|---|---|---|---|---|---|---|---|---|
Js Stack From Scratch | 19,396 | 5 months ago | 1 | January 19, 2017 | 48 | mit | JavaScript | |||
🛠️⚡ Step-by-step tutorial to build a modern JavaScript stack. | ||||||||||
Ice | 17,498 | 721 | 2 days ago | 84 | August 26, 2022 | 266 | mit | TypeScript | ||
🚀 ice.js: The Progressive App Framework Based On React(基于 React 的渐进式应用框架) | ||||||||||
React Loadable | 16,507 | 3,250 | 1,005 | 5 months ago | 30 | August 09, 2018 | 38 | mit | JavaScript | |
:hourglass_flowing_sand: A higher order component for loading components with promises. | ||||||||||
Evergreen | 12,071 | 75 | 87 | 3 days ago | 246 | July 18, 2022 | 54 | mit | JavaScript | |
🌲 Evergreen React UI Framework by Segment | ||||||||||
Awesome Nextjs | 8,436 | 2 days ago | 62 | |||||||
:notebook_with_decorative_cover: :books: A curated list of awesome resources : books, videos, articles about using Next.js (A minimalistic framework for universal server-rendered React applications) | ||||||||||
React I18next | 8,064 | 1,307 | 1,904 | 17 days ago | 269 | September 09, 2022 | 18 | mit | JavaScript | |
Internationalization for react done right. Using the i18next i18n ecosystem. | ||||||||||
Dioxus | 7,965 | 16 | a day ago | 12 | May 03, 2022 | 116 | apache-2.0 | Rust | ||
Friendly React-like GUI library for desktop, web, mobile, and more. | ||||||||||
Rax | 7,870 | 118 | 355 | 20 days ago | 134 | June 09, 2022 | 69 | other | JavaScript | |
🐰 Rax is a progressive framework for building universal application. https://rax.js.org | ||||||||||
Loadable Components | 7,167 | 511 | 374 | 12 days ago | 29 | December 12, 2021 | 21 | mit | JavaScript | |
The recommended Code Splitting library for React ✂️✨ | ||||||||||
React Apollo | 6,900 | 6,511 | 1,074 | 3 years ago | 222 | April 14, 2020 | 202 | mit | JavaScript | |
:recycle: React integration for Apollo Client |
In short: dont try to run js code, and produce a react tree matching pre-rendered one, but use pre-rendered html until js code will be ready to replace it. Make it live.
What else could be done on HTML level? Caching, templatization, and other good things to 🚀, just in a 3kb*.
Render something on server, and use it as HTML on the client
thisIsServer
somewhere, to setup enviroment.will leave trails
, wrapping each block with div with known id.read rendered HTML
back from a page.Bonus - you can store and restore component state.
More details - https://twitter.com/theKashey/status/1021739948536295424
<PrerenderedComponent
// restore - access DIV and get "counter" from HTML
restore={(el) => this.setState({counter: +el.querySelector('i').innerHTML})}
// once we read anything - go live!
live={!!this.state.counter}
>
<p>Am I alive?</p>
<i>{this.props.counter}</i>
</PrerenderedComponent>
Is components HTML was not generated during SSR, and it would be not present in the page code -
component will go live
automatically, unless strict
prop is set.
You may keep some pieces of your code as "raw HTML" until needed. This needed includes:
If your code splitting library (not React.lazy) supports "preload" - you may use it to control code splitting
const AsyncLoadedComponent = loadable(() => import('./deferredComponent'));
const AsyncLoadedComponent = imported(() => import('./deferredComponent'));
<PrerenderedComponent
live={AsyncLoadedComponent.preload()} // once Promise resolved - component will go live
>
<AsyncLoadedComponent />
</PrerenderedComponent>
<PrerenderedComponent
// restore - access DIV and get "counter" from HTML
restore={(_,state) => this.setState(state)}
store={ this.state }
// once we read anything - go live!
live={!!this.state.counter}
>
<p>Am I alive?</p>
<i>{this.props.counter}</i>
</PrerenderedComponent>
Wrap your application with PrerenderedControler
. This would provide context for all nested components, and "scope" counters used to
represent nodes.
This is more Server Side requirement.
import {PrerenderedControler} from 'react-prerendered-component';
ReactDOM.renderToString(<PrerenderedControler><App /></PrerenderedControler>);
!!!!
Without PrerenderedControler
SSR will always produce an unique HTML, you will be not able to match on Client Side.
!!!!
It could be a case - some components should live only client-side, and completely skipped during SSR.
import {render} from 'react-dom';
import { ClientSideComponent } from "react-prerendered-component";
const controller = cacheControler(cache);
const result = render(
<ClientSideComponent>
Will be rendered only on client
</ClientSideComponent>
);
const result = hydrate(
<PrerenderedControler hydrated>
<ClientSideComponent>
Will be rendered only on client __AFTER__ hydrate
</ClientSideComponent>
</PrerenderedControler>
);
ServerSideComponent
simport {clientSideComponent} from 'react-prerendered-component';
export default clientSideComponent(MyComponent);
Partial rehydration could benefit not only SSR-enhanced applications, but provide a better experience for simple code splitting.
In the case of SSR it's quite important, and quite hard, to load all the used chunks before
triggering hydrate
method, or some unloaded parts would be replaced by "Loaders".
Preloaded could help here, if your code-splitting library support preloading
, even if it does not support SSR.
import imported from 'react-imported-component';
import {PrerenderedComponent} from "react-prerendered-component";
const AsyncComponent = imported(() => import('./myComponent.js'));
<PrerenderedComponent
// component will "go live" when chunk loading would be done
live={AsyncComponent.preload()}
>
// until component is not "live" prerendered HTML code would be used
// that's why you need to `preload`
<AsyncComponent/>
</PrerenderedComponent>
Yet again - it works with any library which could preload
, which is literally any library except
React.lazy
.
Prerendered component could also work as a component-level cache. Component caching is completely safe, compatible with any React version, but - absolutely synchronous, thus no Memcache or Redis are possible.
import {renderToString, renderToNodeStream} from 'react-dom/server';
import {
PrerenderedControler,
cacheControler,
CachedLocation,
cacheRenderedToString,
createCacheStream
} from "react-prerendered-component";
const controller = cacheControler(cache);
const result = renderToString(
<PrerenderedControler control={control}>
<CachedLocation cacheKey="the-key">
any content
</CachedLocation>
</PrerenderedControler>
)
// DO NOT USE result! It contains some system information
result === <x-cachedRANDOM_SEED-store-1>any content</x-cachedRANDOM_SEED-store-1>
// actual caching performed in another place
const theRealResult = cacheRenderedToString(result);
theRealResult === "any content";
// Better use streams
renderToNodeStream(
<PrerenderedControler control={control}>
<CachedLocation cacheKey="the-key">
any content
</CachedLocation>
</PrerenderedControler>
)
.pipe(createCacheStream(control)) // magic here
.pipe(res)
Stream API is completely stream and would not delay Time-To-First-Byte
PrerenderedControler
- top level controller for a cache. Requires controler
to be setCachedLocation
- location to be cached.
cacheKey
- string - they key
ttl
- number - time to live
refresh
- boolean - flag to ignore cache
clientCache
- boolean - flag to enable cache on clientSide (disabled by default)
noChange
- boolean - disables cache at all
variables
- object - varibles to use in templatization
as=span
- string, used only for client-side cache to define a wrapper
tag
className
- string, used only for client-side cache
rehydrate
- boolean, used only for client-side cache, false values would keep content as a dead html.
Placeholder
- a template value
name
- a variable nameWithPlaceholder
- renderprop version of Placeholder
NotCacheable
- mark location as non-cacheable, preventing memoizationcacheControler(cache)
- a cache controller factor, requires object with cache
interface to work.
{ get(key): string, set(key, ttl):void }
To optimize rendering performance and reduce memory usage you might use cache templates:
import {CachedLocation, Placeholder, WidthPlaceholder} from 'react-prerendered-component';
const Component = () => (
<CachedLocation key="myKey" variabes={{name: "GitHub", secret: 42 }}>
the <Placeholder name="name"/>`s secret is <Placeholder name="secret"/>
// it's easy to use placeholders in a plain HTML
<WithPlaceholder>
{ placeholder => (
<img src="./img.jpg" alt={placeholder("name") + placeholder("secret")}/>
// but to use it in "string" attribures you have to use render props
)}
</WithPlaceholder>
</CachedLocation>
)
Sometimes you might got something, which is not cacheable. Sometimes cos you better not cache like - like personal information. Sometimes cos it reads data from variable sources and could not be "just cached". It is always hard to manage it. So - just dont cache. It's a one line fix.
import {NotCacheable, notCacheable} from 'react-prerendered-component';
const SafeCache = () => (
<NotCacheable>
<YourComponent />
</NotCacheable>
);
const SafeComponent = notCacheable(YourComponent);
Any network based caches are not supported, the best cache you can use - LRU, is bound to single process, while you probably want multi-threaded(workers) rendering, but dont want to maintain per-instance cache.
You may use nodejs shared-memory libraries (not supported by nodejs itself), like:
Results from rendering a single page 1000 times. All tests executed twice to mitigate possible v8 optimizations.
dry 1013 - dry render to kick off HOT
base 868 - the __real__ rendering speed, about 1.1ms per page
cache 805 - with `cacheRenderedToString` used on uncachable appp
cache 801 - second run (probably this is the "real" speed)
partial 889 - with `cacheRenderedToString` used lightly cached app (the cost of caching)
partial 876 - second run
half 169 - page content cached
half 153 - second run
full 22 - full page caching
full 19 - second run
It is safe to have prerendered
component inside a cached location.
ServerSideComponent
- component to be rendered only on server. Basically this is PrerenderedComponent with live=false
ClientSideComponent
- component to be rendered only on client. Some things are not subject for SSR.
ClientSideComponent
would not be initially rendered with hydrated
prop enabledthisIsServer(flag)
- override server/client flagisThisServer()
- get current environment.Prerendered component is work only once. Once it mounted for a first time.
Next time another UID will be generated, and it will not find component to match. If prerendered-component could not find corresponding component - it goes live automatically.
Idea about PrerenderedComponent is to render something, and rehydrate it back. You should be able to render the same, using rehydrated data.
Until component go live - it's dead HTML code. You may be make it more alive by transforming HTML to React, using html-to-react, and go live in a few steps.
Is this package 25kb? What are you thinking about?
react-progressive-hydration - Google IO19 demo is quite similar to react-prerendered-component
, but has a few differences.
__html:''
+ sCU
hack to prevent HTML updateReact.hydrate
to make component live, breaking connectivity between React TreesMIT