So this is a fairly new topic, React Server Components has recently been released, comparing to SSR/Next.js, how does it affect SEO?
Since the component is rendered in the server dynamically when it is requested, it is not really as static as SSR like Next.js, will search engine fail to index those component if I use it?
A demo can found here
We can see that in api.server.js,
async function renderReactTree(res, props) {
await waitForWebpack();
const manifest = readFileSync(
path.resolve(__dirname, '../build/react-client-manifest.json'),
'utf8'
);
const moduleMap = JSON.parse(manifest);
pipeToNodeWritable(React.createElement(ReactApp, props), res, moduleMap);
}
function sendResponse(req, res, redirectToId) {
const location = JSON.parse(req.query.location);
if (redirectToId) {
location.selectedId = redirectToId;
}
res.set('X-Location', JSON.stringify(location));
renderReactTree(res, {
selectedId: location.selectedId,
isEditing: location.isEditing,
searchText: location.searchText,
});
}
I understand this can help to reduce the workload for client's device, since the component are rendered on the server and sent to the client, and that the component can be rendered with the secret stored in server as we can just pass it in as props rather we sending the secret to client.
But if SEO matters, is SSR preferred over React Server Component?
Server Components are complementary to rendering into HTML, not an alternative. The plan is to have both.
Server Components were not released. What was released is an early tech preview in the spirit of sharing our research. This preview doesn’t include an HTML renderer. The api.server.js file from the demo you mentioned contains a comment about this:
const html = readFileSync(
path.resolve(__dirname, '../build/index.html'),
'utf8'
);
// Note: this is sending an empty HTML shell, like a client-side-only app.
// However, the intended solution (which isn't built out yet) is to read
// from the Server endpoint and turn its response into an HTML stream.
res.send(html);
By the time Server Components are officially released, there will be a streaming HTML renderer for the first render.
It’s not built yet.
It should be same from SEO point of view as SPA.
What happens with classic React SPA is, it loads React components (which are essentially JS functions) as part of the JS bundle, and then it starts to request data from the backend in JSON format. After JSON is fetched, it is rendered via the React component functions and inserted into the DOM. Modern crawlers use V8 engine (or maybe smth else if it's Bing :D), they wait until page is fully loaded and all JSON data is loaded and all components are actually rendered - and then it crawls the resulting DOM.
GoogleBot is crawling SPAs that way for at least 3 years now, probably more - so if you were thinking that SSR is essential for SEO, no, it is not. There were plenty of investigations into this, random example: https://medium.com/#l.mugnaini/spa-and-seo-is-googlebot-able-to-render-a-single-page-application-1f74e706ab11
So essentially for crawler it doesn't really matter, how exactly React component is rendered. In case of React Server Components, component function resides on server and is never transferred to the client as part of the JS bundle. So instead of requesting JSON data, the application requests rendered component in some intermediate format (not HTML btw). Result of that is transferred to the client and is getting rendered to the DOM. So the end result is still the same - it's some DOM elements that the bot can crawl.
Related
I'm using a Headless CMS based in another country that is not near my own, and each request to this CMS takes at least 1.5s to return a payload (which can be a ridiculously long time to wait when you're making multiple requests).
Now, I can use getStaticProps for each page requiring CMS content. But, since I have global components that need information from the CMS too (such as the navigation and footer), this destroys Next's ability to render each page statically, and it forces me to use getInitialProps in _app.js.
Now, before you say:
Just create a request to the CMS on every page using getStaticProps.
That will remove the ability for my website to be a proper single-page application and cause a re-render for elements in the layout that don't need to be re-mounted while navigating. This ability to navigate without re-mounting is vital since the navigation has functionality that relies on it.
Here is an example of what my _App.getInitialProps looks like:
const appProps = await App.getInitialProps(context);
try{
// Retrieve data from CMS.
// Yes. I do have to create two requests to the CMS to retrieve a full payload,
// as that is how their API is setup, unfortunately.
const dataOne = await fetch(...);
const dataTwo = await fetch(...);
// Yes, I'm aware this isn't actually how it would work.
return { ...dataOne, ...dataTwo, ...appProps };
} catch(error){
console.log("Error in _App component:", error);
}
return appProps;
The ideal workaround for this would be to use getStaticProps in each component that requires it - but this isn't a feature that Next offers, unfortunately.
I am currently building an app with React and Node.js
In this app, I need to query a database on my own server with the following function, located in a separate file called "database.js"
const fetchQuery = util.promisify(con.query).bind(con)
// Get all the tracks for a given date from the
const fetchTracks = async (date) => {
const rows = await fetchQuery("SELECT * FROM tracks WHERE playlistDate = '"+date+"'");
}
This works perfectly when I run the file with Node from the command line. However, when I attempt to import it into my react app with
import { fetchTracks, addTracks } from '../scripts/database'
I begin to get errors in the database file, specifically Unhandled Rejection (TypeError): Net.createConnection is not a function on my fetchQuery call.
From what I've read, this happens when attempting to call the function from the browser, as that would pose a security risk. However, as I understand it, all node operations are performed on the server side, right? Why would I be getting this flag when the database is supposedly queried before the page is served? What do I need to do amend this?
React runs in the browser, so as soon as you include database.js in your React app, it's running in the browser with the rest of React, so of course you'll get the error. Making this work gives two options:
expose an API endpoint from Node for React to call, and that API endpoint calls database.js, or,
investigate server-side rendering for React, where some of your React app -- particular the more static parts of it like your main menu -- are created on the Node side and only the final HTML is sent to the browser. (This is a large topic all by itself though.)
By your example which takes a date parameter for the SQL, I'm guessing #2 isn't an option.
Do you know if it's possible to re-execute Gatsby page queries (normal queries) manually?
Note, This should happen in dev mode while gatsby develop runs.
Background info: I'm trying to set up a draft environment with Gatsby and a Headless CMS (Craft CMS in my case). I want gatsby develop to run on, say, heroku. The CMS requests a Gatsby page, passing a specific draft-token as an URL param, and then the page queries should be re-executed, using the token to re-fetch the draft content from the CMS rather than the published content.
I'm hooking into the token-request via a middleware defined in gatsby-config.js. This is all based on https://gist.github.com/monachilada/af7e92a86e0d27ba47a8597ac4e4b105
I tried
createSchemaCustomization({ refresh: true }).then(() => {
sourceNodes()
})
but this completely re-creates all pages. I really only want the page queries to be extracted/executed.
Probably you are looking for this. Basically, you need to set an environment variable (ENABLE_GATSBY_REFRESH_ENDPOINT) which opens and exposes a /__refresh webhook that is able to receive POST requests to refresh the sourced content. This exposed webhook can be triggered whenever remote data changes, which means you can update your data without re-launching the development server.
You can also trigger it manually using: curl -X POST http://localhost:8000/__refresh
If you need a detailed explanation of how to set .env variables in Gatsby just tell me and I will provide a detailed explanation. But you just need to create a .env file with your variables (ENABLE_GATSBY_REFRESH_ENDPOINT=true) and place this snippet in your gatsby-config.js:
require("dotenv").config({
path: `.env.${activeEnv}`,
})
Of course, it will only work under the development environment but in this case, it fits your requirements.
Rebuild for all is needed f.e. when you have indexing pages.
It looks like you need some logic to conditionally call createPage (with all data refetched) or even conditionally fetch data for selected pages only.
If amount (of pages) is relatively not so big I would fetch for all data to get page update times. Then in loop conditionally (time within a few minutes - no needs to pass parameter) call createPage.
If develop doesn't call 'createPage' on /__refresh ... dive deeper into gatsby code and find logic and way to modify redux touched nodes.
... or search for other optimization techniques you can use for this scenario (queried data cached into json files?).
I'm looking for web framework for interactive, real-time web apps without writing the Client (Browser), everything will be done by the Server.
There's such framework - LiveView in Phoenix (Elixir/Erlang), see demos below. I'm looking for something similar in JavaScript/TypeScript or Ruby.
How it works, it's better to demonstrate by example. Let's imagine we already have such framework in JavaScript and building an Autocomplete component. It would look almost like React.JS, but with the huge difference - it will be run on the Server:
class Autocomplete extends MagicServerSideReactComponent {
state = {
query: '',
suggestions: []
}
async search(e) {
const query = e.target.value
// This whole component runs on the Server, not in the Browser.
// So it has full access to the Server infrastructure and Database.
const suggestions = await db.find(`select like '${query}'`)
this.setState({ query, suggestions })
}
render() {
return <div>
<input value={this.state.query} onKeyPress={this.search}/>
{this.state.suggestions.map((name) => <div>{name}</div>)}
</div>
}
}
How it works:
When rendered first time:
- Autocomplete component get rendered on the Server and final HTML sent
to the Browser.
The Server remembers the `state` and the Virtual DOM - i.e. it's a
statefull Server, not usual Stateless node.js Server.
- Browser gets the HTML and renders it into DOM.
When user type something in the input:
- Browser serializes the Event and sends it to the Server,
something like `{ method: 'Autocomplete.search', event: '...' }`
- Server get the serialized Event and the previously stored Virtual DOM
for the given Web Page.
Then Server process the Event, and as a result the Virtual DOM
on the Server gets updated.
Then Server finds out the difference between the new and old Virtual DOM
and generates the DIFF.
Then Server sends the DOM DIFF to the Browser
- Browser gets the DOM DIFF and modify its DOM.
And the user see its Web Page updated with the search suggestions.
Do you know any such web frameworks in JavaScript or Ruby?
Please don't suggest frameworks that do something like that - but where you have to mutate DOM manually. Virtual DOM on the Server is important because it allows to update DOM automatically. It doesn't have to be exactly like React.JS but it should update DOM automatically, like React does.
P.S.
Why? Because of the first law of distributed systems - "Don't build distributed systems". It's simpler to build web app as one instead of distributing it into client and server.
Latency - yes, nothing is free, you have to pay for the simplicity and the latency will be the price. The interactions would be delayed - to travel to the server and back.
Performance - yes, Server is not stateless anymore, it's stateful, runs Virtual DOM and consume more resources.
You can take a look at marko by ebay (https://markojs.com/docs/server-side-rendering/) but I don't think you can find a framework/library with all your requirements.
Because of the first law of distributed systems - "Don't build distributed systems". It's simpler to build web app as one instead of distributing it into client and server.
The code you send to user with react or any other single page application framework is defining view. So you shouldn't consider it as a system. It's just html, css, js + user's data
Virtual DOM on the Server is important because it allows to update DOM
automatically.
Why the goal is to update DOM? DOM is just a view of your state/data. And your frontend application is just a mapper/hash function from your state to DOM. So if you only have your state in your server, you kind of have your DOM as well.
If you don't want to write both server and client, but still you want to have fancy frontend features with thousands of open source repos, you can try react + firebase.
I have a Java Spring Application with a Tomcat server that listen on kafka topic. I want to display all messages in a real-time mode on the web page. Therefore, when a kafka messages is arrived in the backend I want to see it on my web page. I don't know a good approach to push kafka message directly to the front-end and display it on web page. Is someone could help my with a solution and some examples that could help? Thanks!
I have implemented a system like this in Java for my last employer, albeit not with Spring/Tomcat. It was consuming messages from Kafka and serving them on a web socket to be displayed in the browser. The approach I followed was to use akka-stream-kafka and akka-http for web-socket support. The benefit of that is both are based on akka-streams which makes it an easy fit for streaming data.
While you can embed akka-http in your spring app running inside tomcat, it may not feel the most natural choice any more as spring framework already has its own support for both kafka and websockets. However, if you're not familiar with either, then jumping on the akka approach may be easiest and the core logic goes along these lines (I can't share the code from work so have just put this together from the examples in the docs, not tested):
public Route createRoute(ActorSystem system) {
return path("ws", () -> {
ConsumerSettings<byte[], String> consumerSettings = ConsumerSettings.create(system, new ByteArrayDeserializer(), new StringDeserializer())
.withBootstrapServers("localhost:9092")
.withGroupId(UUID.randomUUID().toString()) //this is so that each client gets all messages. To be able to resume from where a client left off in case of disconnects, you can generate in on the client side and pass in the request
.withProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest")
return handleWebSocketMessages(
Flow.fromSinkAndSourceCoupled(
Sink.ignore(),
Consumer.committableSource(consumerSettings, Subscriptions.topics("topic1"))
.map(msg -> TextMessage.create(msg.record().value()))
)
);
}
}
To expose this route you can follow the minimalistic example, the only difference being the route you define needs the ActorSystem:
final Http http = Http.get(system);
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Flow<HttpRequest, HttpResponse, NotUsed> routeFlow = createRoute(system).flow(system, materializer);
final CompletionStage<ServerBinding> binding = http.bindAndHandle(routeFlow,
ConnectHttp.toHost("localhost", 8080), materializer);
Once you have your messages published to the websocket, the front end will code will of course depend on your UI framework of choice, the simplest code to consume ws messages from javascript is:
this.connection = new WebSocket('ws://url-to-your-ws-endpoint');
this.connection.onmessage = evt => {
// display the message
To easily display the message in the UI, you want the format to be something convenient, like JSON. If your Kafka messages are not JSON already, that's where the Deserializers in the first snippet come in, you can convert it to a convenient JSON string in the Deserializer or do it later on in the .map() called on the Source object.
Alternatively, if polling is an option you can also consider using the off-the-shelf Kafka Rest Proxy, then you only need to build the front-end.