React Tour
Routing
Similarly to Next.js,
we define a new page by creating a new +Page.jsx
file.
// /pages/index/+Page.jsx
// Environment: client and server
import React from 'react'
import { Counter } from '../components/Counter'
export { Page }
function Page() {
return (
<>
This page is rendered to HTML and interactive: <Counter />
</>
)
}
// /pages/index/+Page.tsx
// Environment: client and server
import React from 'react'
import { Counter } from '../components/Counter'
export { Page }
function Page() {
return (
<>
This page is rendered to HTML and interactive: <Counter />
</>
)
}
By default, Vike does Filesystem Routing.
FILESYSTEM URL
/pages/index/+Page.jsx /
/pages/about/+Page.jsx /about
We can also define a page's route with a Route String (for parameterized routes such as /movies/@id
) or a Route Function (for full programmatic flexibility).
// /pages/index/+route.js
// Note how the two files share the same folder `/pages/index/`; this is how Vike
// knows that `/pages/index/+route.js` defines the route of `/pages/index/+Page.jsx`.
// Route Function
export default (pageContext) => pageContext.urlPathname === '/'
// If we don't create a `+route.js` file then Vike does Filesystem Routing
// /pages/index/+route.ts
// Note how the two files share the same folder `/pages/index/`; this is how Vike
// knows that `/pages/index/+route.ts` defines the route of `/pages/index/+Page.tsx`.
import type { PageContext } from 'vike/types'
// Route Function
export default (pageContext: PageContext) => pageContext.urlPathname === '/'
// If we don't create a `+route.ts` file then Vike does Filesystem Routing
Render Control
Unlike Next.js, we control how our pages are rendered.
// /renderer/+onRenderHtml.jsx
// Environment: server
export { onRenderHtml }
import ReactDOMServer from 'react-dom/server'
import React from 'react'
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
async function onRenderHtml(pageContext) {
const { Page, data } = pageContext
const viewHtml = ReactDOMServer.renderToString(<Page {...data} />)
const title = 'Vite SSR'
return escapeInject`<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
</head>
<body>
<div id="root">${dangerouslySkipEscape(viewHtml)}</div>
</body>
</html>`
}
// /renderer/+onRenderHtml.tsx
// Environment: server
export { onRenderHtml }
import ReactDOMServer from 'react-dom/server'
import React from 'react'
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import type { PageContextServer } from 'vike/types'
async function onRenderHtml(pageContext: PageContextServer) {
const { Page, data } = pageContext
const viewHtml = ReactDOMServer.renderToString(<Page {...data} />)
const title = 'Vite SSR'
return escapeInject`<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
</head>
<body>
<div id="root">${dangerouslySkipEscape(viewHtml)}</div>
</body>
</html>`
}
// /renderer/+onRenderClient.jsx
// Environment: client
export { onRenderClient }
import ReactDOM from 'react-dom/client'
import React from 'react'
async function onRenderClient(pageContext) {
const { Page, data } = pageContext
ReactDOM.hydrateRoot(document.getElementById('root'), <Page {...data} />)
}
// /renderer/+onRenderClient.tsx
// Environment: client
export { onRenderClient }
import ReactDOM from 'react-dom/client'
import React from 'react'
import type { PageContextClient } from 'vike/types'
async function onRenderClient(pageContext: PageContextClient) {
const { Page, data } = pageContext
ReactDOM.hydrateRoot(document.getElementById('root'), <Page {...data} />)
}
This control enables us to easily and naturally integrate any tool we want (Redux, GraphQL, Service Worker, Preact, ...).
Note how we defined the files onRenderClient.jsx
and onRenderHtml.jsx
in a directory called /renderer/
: that way, we tell Vike to apply the onRenderHtml()
and onRenderClient()
hooks to all our pages.
This means we can now create a new page just by defining a new +Page.jsx
file (the +route.js
file is optional).
Plus files in /renderer/
can be overridden. For example, we can override the onRenderHtml()
and onRenderClient()
hooks for rendering some of
our pages with a completely different UI framework such as Vue.
Data Fetching
Let's now have a look at how to fetch data.
// /pages/star-wars/movie/+Page.jsx
// Environment: client and server
export { Page }
import React from 'react'
function Page(data) {
const { movie } = data
return (
<>
<h1>{movie.title}</h1>
<p>Release Date: {movie.release_date}</p>
<p>Director: {movie.director}</p>
</>
)
}
// /pages/star-wars/movie/+Page.tsx
// Environment: client and server
export { Page };
import React from 'react'
import type { Data } from './+data'
function Page(data: Data) {
const { movie } = data
return (
<>
<h1>{movie.title}</h1>
<p>Release Date: {movie.release_date}</p>
<p>Director: {movie.director}</p>
</>
)
}
// /pages/star-wars/movie/+route.js
// Environment: server
// Route String
export default '/star-wars/@movieId'
// /pages/star-wars/movie/+route.ts
// Environment: server
// Route String
export default '/star-wars/@movieId'
// /pages/star-wars/movie/+data.js
// Environment: server
export { data }
import fetch from 'node-fetch'
async function data(pageContext) {
// The route parameter of `/star-wars/@movieId` is available at `pageContext.routeParams`
const { movieId } = pageContext.routeParams
// By default, +data.js files are loaded and executed only on the server-side
// which means we can use SQL/ORM queries here.
const response = await fetch(`https://star-wars.brillout.com/api/films/${id}.json`)
let movie = await response.json()
// Our render and hydrate functions we defined earlier pass `pageContext.data` to
// the root React component `Page`; this is where we define it.
return {
movie
}
}
// /pages/star-wars/movie/+data.ts
// Environment: server
export { data }
export type Data = Awaited<ReturnType<typeof data>>
import fetch from 'node-fetch';
import type { PageContextServer } from 'vike/types'
async function data(pageContext: PageContextServer) {
// The route parameter of `/star-wars/@movieId` is available at `pageContext.routeParams`
const { movieId } = pageContext.routeParams;
// By default, +data.ts files are loaded and executed only on the server-side
// which means we can use SQL/ORM queries here.
const response = await fetch(`https://star-wars.brillout.com/api/films/${id}.json`)
let movie = await response.json();
// Our render and hydrate functions we defined earlier pass `pageContext.data` to
// the root React component `Page`; this is where we define it.
return {
movie
}
}
That's it for the tour and we have actually already seen most of Vike's interface; not only is Vike flexible but it's also simple to use!