React Server Side Rendering with Sass - javascript

As i have searched through the previous but could not find any relevant / solved issues on this so I ask...
Current stack: React, express/node, Sass, Webpack, Sass.
I have a question specific to the Styling of pages before server sends them to the browser.
Styling is working perfectly in the browser and by I mean when the pages are being navigated via browser router, all the styling works. However, I need to figure out a way to inject custom Sass styles to the page before the Server sends the page to the browser.
Current behavior: I access any page from the browser, I get serve the page with correct styling BUT there is a minor delay to get the correct styling in place to appear on the page. If view in slow motion, page appears without styling (because the server sent the page with no styling) and then browser takes over with correct styling.
I have tried webpack to generate all styles in a separate file but I can't seem to link this file to page before server sends it to the browser.
Any help / feedback will be appreciated?
server.js(route handler)
app.get('*', (req, res) => {
const store = createStore();
const promises = matchRoutes(Routes, req.path).map(({ route }) => {
return route.loadData ? route.loadData(store) : null;
});
Promise.all(promises)
.then(() => {
const context = {};
const content = renderer(req, store, context);
if (context.notFound) res.status(404);
res.send(content);
});
});
const PORT = process.env.PORT || 3000;
renderer.js
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter, matchPath } from 'react-router-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import serialize from 'serialize-javascript';
import Routes from '../client/Routes';
export default (req, store, context) => {
// req.path is the url that the user is trying to access
const content = renderToString(
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<div>
{ renderRoutes(Routes) }
</div>
</StaticRouter>
</Provider>
);
return `
<html>
<head>
<link rel="stylesheet" href="../../public/styles.css">
</head>
<body>
<div id='app'>${content}</div>
<script>
window.INITIAL_STATE = ${serialize(store.getState())}
</script>
<script src="bundle.js"></script>
</body>
</html>
`;
};
webpack.config.js (I omitted the rest of config since its not relevant)
const extractSass = new ExtractTextPlugin({
filename: "styles.css",
disable: process.env.NODE_ENV === "development"
});
module: {
rules: [{
test: /\.scss$/,
use: extractSass.extract({
use: [{
loader: "css-loader"
}, {
loader: "sass-loader"
}],
// use style-loader in development
fallback: "style-loader"
})
}]
},
plugins: [ extractSass ],
This gives me a styles.css file in my /build folder. How can i link this to my html. If i try to throw a <link> tag the browser does not recognize the file.

I finally solved this issue by creating another route handler.
app.get('/styles.css', (req, res) => {
res.sendFile(__dirname + "/" + "styles.css");
});
My webpack compiles a styles.css file which gets put in the /public folder. Please note i changed the path of my styles file in webpack to this: ../public/styles.css
app.use(express.static('public'));
Above statement ^^^ makes the styles.css file accessible and finally styles are applied by supplying the below <link> tag in the response of server to the browser.
<link rel="stylesheet" href="../../public/styles.css">
If anyone else have a better solution, I would love to get feedback.

Related

Can't load Expo Vector Icons in Nextjs

I have tried a lot of different ways to do this, with absolutely zero luck, over multiple days.
I am trying to use Solito Nativebase Universal Typescript repo to do this:
https://github.com/GeekyAnts/nativebase-templates/tree/master/solito-universal-app-template-nativebase-typescript
I have read, and tried everything on this page at least a dozen times:
https://github.com/GeekyAnts/nativebase-templates/issues/43
My current next.config.js file looks like this:
/** #type {import('next').NextConfig} */
const { withNativebase } = require('#native-base/next-adapter')
const withImages = require('next-images')
const { withExpo } = require('#expo/next-adapter')
const withFonts = require('next-fonts')
module.exports = withNativebase({
dependencies: [
'#expo/next-adapter',
'next-images',
'react-native-vector-icons',
'react-native-vector-icons-for-web',
'solito',
'app',
],
plugins: [
[withFonts, { projectRoot: __dirname }],
withImages,
[withExpo, { projectRoot: __dirname }],
],
nextConfig: {
images: {
disableStaticImages: true,
},
projectRoot: __dirname,
reactStrictMode: true,
webpack5: true,
webpack: (config, options) => {
config.resolve.alias = {
...(config.resolve.alias || {}),
'react-native$': 'react-native-web',
'#expo/vector-icons': 'react-native-vector-icons',
}
config.resolve.extensions = [
'.web.js',
'.web.ts',
'.web.tsx',
...config.resolve.extensions,
]
return config
},
},
})
I have also tried using #native-base/icons, again, no luck.
My end use case is this:
export const Cart = (props: IIconStyles) => {
return (
<Icon
as={FontAwesome5}
name="shopping-cart"
size={props.size ? props.size : 6}
color="gray.200"
/>
)
Theoretically it SHOULD show a shopping cart, but instead, this is what I see:
So clearly there's some font issue or other issue that is preventing it from loading in the actual SVG.
I can't figure out what this is - I've tried rewriting my _document.tsx file like this:
https://docs.nativebase.io/nb-icons
I've tried adding this to my next.config.js:
config.module.rules.push({
test: /\.ttf$/,
loader: "url-loader", // or directly file-loader
include: path.resolve(__dirname, "node_modules/#native-base/icons"),
});
When I try to do something like this:
import fontsCSS from '#native-base/icons/FontsCSS';
in my _document.tsx file, I get the following error:
Module not found: Can't resolve '#native-base/icons/lib/FontsCSS'
Despite the fact that I've got #native-base/icons installed in my package.json, as well as having it in my Babel file per the instruction link above.
How do I get vector icons to work in Next?
Note, this is specifically Next/Expo/React Native
You can read more about setup of next-adapter-icons here.
I got it working with following approach,
next.config.js
const { withNativebase } = require("#native-base/next-adapter");
const path = require("path");
module.exports = withNativebase({
dependencies: ["#native-base/icons", "react-native-web-linear-gradient"],
nextConfig: {
webpack: (config, options) => {
config.module.rules.push({
test: /\.ttf$/,
loader: "url-loader", // or directly file-loader
include: path.resolve(__dirname, "node_modules/#native-base/icons"),
});
config.resolve.alias = {
...(config.resolve.alias || {}),
"react-native$": "react-native-web",
"react-native-linear-gradient": "react-native-web-linear-gradient",
"#expo/vector-icons": "react-native-vector-icons",
};
config.resolve.extensions = [
".web.js",
".web.ts",
".web.tsx",
...config.resolve.extensions,
];
return config;
},
},
});
pages/_document.js
import React from 'react';
import { DocumentContext, DocumentInitialProps } from 'next/document';
import { default as NativebaseDocument } from '#native-base/next-adapter/document'
// Icon Font Library Imports
import MaterialIconsFont from '#native-base/icons/FontsCSS/MaterialIconsFontFaceCSS';
import EntypoFontFaceCSS from '#native-base/icons/FontsCSS/EntypoFontFaceCSS';
const fontsCSS = `${MaterialIconsFont} ${EntypoFontFaceCSS}`;
export default class Document extends NativebaseDocument {
static async getInitialProps(ctx) {
const props = await super.getInitialProps(ctx);
const styles = [
<style key={'fontsCSS'} dangerouslySetInnerHTML={{ __html: fontsCSS }} />,
...props.styles,
]
return { ...props, styles: React.Children.toArray(styles) }
}
}
pages/index.tsx
import React from "react";
import { Box, Icon } from "native-base";
import Entypo from "#expo/vector-icons/Entypo";
export default function App() {
return (
<Box>
<Icon
as={Entypo}
name="user"
color="coolGray.800"
_dark={{
color: "warmGray.50",
}}
/>
</Box>
);
}
Using import like this:
import MaterialIcons from '#expo/vector-icons/MaterialIcons'
in place of:
import { MaterialIcons } from '#expo/vector-icons'
worked for me. I think this is because of the way babel/webpack handles imports in the template. I followed the steps here to setup the icons.
Here's what that looks like on web:

How to construct js object in React

I have some problems with React and Advertisement.
Wanna use 'Coupang' Advertisement, but they support the script library only.
I can add it to 'index.html' in the public directory, but cannot customize the location.
here is the code,
<script src="https://ads-partners.coupang.com/g.js"></script>
<script>
new PartnersCoupang.G({"id":23232,"subId":null});
</script>
It's a dynamic advertisement.
How can I add it to the React functional component??
MB this will be helpful.
useScript will help you add script to your code dynamically.
and you custom hook (just create PartnersCoupang);
const usePartnersCoupang = () => {
const const [loaded, error] = useScript('https://ads-partners.coupang.com/g.js');
React.useEffect(() => {
if (loaded) {
new PartnersCoupang.G({"id":23232,"subId":null});
}
}, [loaded]);
React.useEffect(() => {
if (error) {
console.error('PartnersCoupang Failed.');
}
}, [error]);
}
Actually, you should eject from your create-react-app project by this command:
$ yarn eject
or:
$ npm run eject
Then you can see a folder that name is config, in this folder you can see all configuration of your project, especially your Webpack configs, then you should add your external library to the webpack as external key on the configuration of Webpack:
// webpack.config.js
...
module.exports = {
...
externals: {
PartnersCoupang: '[path-to]/g.js',
},
...
};
Then in your component import it easily:
import React, { Component } from 'react';
import PartnersCoupang from 'PartnersCoupang';
class YourComponent extends Component {
componentDidMount() {
new PartnersCoupang.G({"id":23232,"subId":null});
}
}

CSS module is not working in react 16.13.1

I want to style components in react JS. I tried several ways to do that. When I try to create a CSS object and apply it to the component in the same JS file, then it is working. But when I try to apply CSS from external CSS file and import it in the JS file then it is not working. And I have also tried to save that file as filename.module.css. But it didn't help me.
The list of installed node modules and their versions is given below.
> #material-ui/core#4.9.11
> #material-ui/icons#4.9.1
> firebase#7.14.1
> react#16.13.1
> react-dom#16.13.1
> react-router-dom#5.1.2
> react-scripts#3.4.1
In webpack.config.js file of react-script module, I found below code:
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
So I guess my react project is supporting CSS files as well as SCSS and SASS files. Did I understand correctly?
header.module.css file:
.heading {
color: '#3592EF';
font-weight: '600';
letter-spacing: '2px';
}
.navButton {
color: '#444';
font-size: '16px';
padding: '4px 8px';
margin: 'auto';
margin-right: '15px';
}
Header.js file:
import React from 'react';
import { Button } from '#material-ui/core';
import styles from './header.module.css';
export default class Header extends React.Component {
render() {
return (
<div>
<span className={styles.headling}>Heading element</span>
<Button className={styles.navButton}>Home</Button>
<Button className={styles.navButton}>About</Button>
</div>
);
}
}
The output is coming with the Heading element and Home, About button. But without CSS style.
How can I solve this issue?
Thank you.
CSS module is included in react
all you have to do is building a file with the correct name like "example.module.css" and import it with the correct path if it's in the same folder `import tst from 'example.module.css' or whatever path it is in, you can replace "tst" with any any name you like.
and then you can use it in className like
<button className={tst.buttonPrimary}>Submit /button>
this video can help you more:
https://www.youtube.com/watch?v=Udf951dyTdU
Generally custom components do not accept className prop if it is not propagated to the inner of the component.
Looking in the material ui react components Button documentation, this component cannot have className property.
https://material-ui.com/components/buttons/
It means, you cannot use it. To convince your self, try to use general html <button> and it will work, you see.
Edit: grammar
first : open your terminal and run "npm install css-loader style-loader --save-dev"
These loaders enable Webpack to bundle CSS files
Second: in your webpack.config.js file, add the new loaders for interpreting CSS files:
module.exports = {
...
module: {
rules: [
...
*///
{
test: /\.css$/i,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
*////
],
},
...
};

Unable to get dynamic component in Next.js to skip server side rendering and show only on the client

I am trying to dynamically render a custom react component containing react-owl-carousel in a next js application.
However, due to the nature of the react-owl-carousel it cannot be server rendered. So i decided to skip server side rendering for the dynamic import by setting ssr to false like so:
const Testimonials = dynamic(
() => import('../components/home/Testimonials'),
{
ssr: false,
loading: () => <p>...loafing</p>
}
)
The complete Home component looks like this:
import React from 'react';
const Testimonials = dynamic(
() => import('../components/home/Testimonials'),
{
ssr: false,
loading: () => <p>...loafing</p>
}
)
export default class Home extends Component {
render () {
return (
<div>
<Testimonials />
</div>
);
}
The Testimonial component looks like so:
import React from 'react';
import OwlCarousel from 'react-owl-carousel';
import 'owl.carousel/dist/assets/owl.carousel.css';
import 'owl.carousel/dist/assets/owl.theme.default.css';
const Testimonials = () => {
return (
<div>
<p>skjdjks djks dk sjdk</p>
<OwlCarousel
className="owl-theme"
loop
margin={10}
nav
>
<div class="item"><h4>1</h4></div>
<div class="item"><h4>2</h4></div>
<div class="item"><h4>3</h4></div>
<div class="item"><h4>4</h4></div>
<div class="item"><h4>5</h4></div>
<div class="item"><h4>6</h4></div>
<div class="item"><h4>7</h4></div>
<div class="item"><h4>8</h4></div>
<div class="item"><h4>9</h4></div>
<div class="item"><h4>10</h4></div>
<div class="item"><h4>11</h4></div>
<div class="item"><h4>12</h4></div>
</OwlCarousel>
</div>
);
}
export default Testimonials;
However, i have been battling with the dynamic component to load on the client side for about 4 hours now. On the client side it shows only the loading indicator. if i remove the ssr option it tries to server render the component and throws an error. Please find attached a screenshot showing what i am currently seeing on the client side.
UPDATE: if i comment out everything in the 'Testimonials' component that has to do with OWL Carousel, the component shows the loading indicator and is rendered in the client side. So i am guesing the issue is with Owl carousel. not sure what it is tho yet.
Eventually, after much debugging and trying different things i was able to get it to work by following these steps:
1) Install jquery npm install jquery --save
2) Update the next.config.js file to include jquery via the webpack's provide plugin like
so:
const withCSS = require('#zeit/next-css')
const withLess = require('#zeit/next-less')
const webpack = require('webpack');
const withFonts = require('next-fonts');
module.exports = withFonts(withLess(withCSS(
{
webpack: function (config) {
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]'
}
}
})
config.plugins.push(new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}))
return config
}
}
)));
In my case i am also using these next js module: #zeit/next-css #zeit/next-less, hence my configuration looks like so.

angular 2 equivalent of react renderToString

I am looking for the angular 2 equivalent of react-dom/server.renderToString
import { renderToString } from 'react-dom/server';
// Render the component to a string
const html = renderToString(
<App />
);
What is the simplest code example to transform a directive/component into HTML using NodeJs?
I guess it should be possible with one of these packages:
#angular/compiler - v2.0.0-rc.2
#angular/platform-server -
v2.0.0-rc.2
Angular Components use HTML templates as part of the development process, but we actually compile those templates to Javascript, rather than HTML in order to render them to the browser.
The closest thing that might help you would be to look at https://github.com/angular/universal. This project represents the best way to run your Angular components on the server in a way that renders javascript and HTML and delivers it to the browser via the network.
We can't supply a simple method to "render" to HTML, because any sort of "rendering" involves the entire Angular framework (core, common, template compilation) and processing your entire component tree.
Hope this helps!
import 'angular2-universal/polyfills';
import {
provide,
REQUEST_URL,
ORIGIN_URL,
NODE_ROUTER_PROVIDERS,
NODE_HTTP_PROVIDERS,
Bootloader
} from 'angular2-universal';
import { APP_BASE_HREF } from '#angular/common';
const bootloader = new Bootloader({
platformProviders: [
{provide: ORIGIN_URL, useValue: 'http://localhost:3000'},
{provide: APP_BASE_HREF, useValue: '/'}
],
async: true,
preboot: false // { appRoot: 'app-root' } // your top level app component selector
});
export function ngApp(req, res) {
let url = req.originalUrl || '/';
const config = {
template: `<!doctype html><html><head>...</head><body>...</body></html>`,
directives: [AppComponent],
providers: [
{provide: REQUEST_URL, useValue: url},
...NODE_ROUTER_PROVIDERS,
...NODE_HTTP_PROVIDERS
]
};
bootloader.serializeApplication(config)
.then(html => res.send(html));
}

Categories