I have a preact SSR app and have used Emotion JS 10 for styling.
I wanted to add RTL support with this so have used createEmotion
and createEmotionServer
and used the resulting renderStylesToString
to render the app.
However while creating createEmotion
it requires the plugins to be used for which i added the stylis-rtl
plugin but this would always apply the RTL styles while i want the RTL styles to be applied on the basis of each request.
I could not find any way to tell emotion to apply and get the styles for RTL on each request basis.
They do have straight forward implementation for React 16 in which you can pass multiple caches for each request to the CacheProvider
.
But i can't seem to figure this out for Preact.
One solution could be to serve different webpack builds for RTL but that would be unnecessary overhead.
EDIT 1: for anyone looking to implement something similar, this is my approach
Client Side as well as the css
imports on the server:
import stylisRTL from 'stylis-rtl';
import createEmotion from 'create-emotion';
const {
cx: cxRTL,
injectGlobal: injectGlobalRTL,
css: cssRTL,
cache: cacheRTL,
keyframes: keyframesRTL
} = createEmotion({
key: 'c',
stylisPlugins: [stylisRTL]
});
const {
cx: cxLTR,
injectGlobal: injectGlobalLTR,
css: cssLTR,
cache: cacheLTR,
keyframes: keyframesLTR
} = createEmotion({
key: 'c',
stylisPlugins: []
});
const runForBoth = (rtlFn, ltrFn) => (...args) => {
//this would be ur store sent in html to check whether it is in rtl or ltr mode
const isRTL = typeof window !== 'undefined' && window.__PRELOADED_STATE__.shell.RTL;
let result;
if (__BROWSER__) {
if (isRTL) {
result = rtlFn(...args);
} else {
result = ltrFn(...args);
}
} else {
result = ltrFn(...args);
rtlFn(...args);
}
return result;
};
export const cx = runForBoth(cxRTL, cxLTR);
export const injectGlobal = runForBoth(injectGlobalRTL, injectGlobalLTR);
export const css = runForBoth(cssRTL, cssLTR);
export const keyframes = runForBoth(keyframesRTL, keyframesLTR);
export const cacheEmotionLTR = cacheLTR;
export const cacheEmotionRTL = cacheRTL;
and for SSR:
const { renderStylesToString: renderStylesToStringLTR } = createEmotionServer(cacheEmotionLTR);
const { renderStylesToString: renderStylesToStringRTL } = createEmotionServer(cacheEmotionRTL);
i create 2 cache and based on request headers i decide which renderStylesToString
to use
The one concern i have is runForBoth
is technically wrong. But it is working as of now.
I did not want to change the way css
Fn is imported as now i can just use this custom-emotion imports and alias it in webpack
If you want to make this per request then you should create 2 emotion instances (one with the plugin and one without it). Additionally, you would have to use correct instance for each request - the easiest way for this would be probably with context: put emotion in the context and use it from there instead of from import statement.