This project is a template for quickly starting a Server-Side Rendering (SSR) Node.js application, combining a powerful set of vite, react, effector, and fastify.
Press "Use this template" to create a copy of repository.
Clone your repository:
git clone https://github.com/effector/vike-react-template my-app
# OR
gh repo clone effector/vike-react-template my-app
cd my-app
corepack enable
corepack prepare
pnpm install
pnpm dev
pnpm build
pnpm start
Strongly recommend to carefully review each file in this repository, before using it in production.
This project inherites Vike project structure:
dist/
pages/
public/
renderer/
server/
src/
dist
contains result of pnpm build
, it is production build;pages
is a Vike's filesystem routing;public
is a static files directory;renderer
is a react + effector integration hooks;server
is a fastify server, builds with tsc
, runs with ts-node
;src
is a FSD basis, with code imported into pages
, and renderer
;pages/
pages/+Wrapper.tsx
It is a data provider for logic uses effector.
pages/+Layout.tsx
To wrap up components with some layout use +Layout.tsx
.
Also, you can nest multiple layouts.
pages/index/+pageStarted.ts
This vike hook describes what event Vike should call on server when page logic can be started.
Usually looks like this:
import { createPageStart } from "~/shared/init";
export const pageStarted = createPageStart();
// pageStarted has type:
pageStarted: EventCallable<{
params: Record<string, string>;
data: void;
}>;
params
looks like { id: "foo" }
for route pages/example/@id
and pathname /example/foo
.
Url | Route | params |
---|---|---|
/ | pages/index | {} |
/example/100 | pages/example/@id | { id: "100" } |
pages/index/+Page.tsx
This is a page component. It can import model.ts
and all from src using ~/
alias.
Use export default
and named functions:
export default function PageHome() {
return <h1>Hello World</h1>;
}
pages/index/model.ts
This is a logic file written in effector. It can import +pageStarted.ts
and all from src using ~/
alias.
import { createEffect, sample } from "effector";
import { pageStarted } from "./+pageStarted";
const helloFx = createEffect((name: string) => {
console.info(`Hello ${name}`);
});
sample({
clock: pageStarted,
fn: () => "World",
target: helloFx,
});
When user opened http://localhost:3000, pageStarted
fired, then sample with clock: pageStarted
reacts and triggers helloFx
with "World"
argument.
In our dynamic and event driven kind of environment, this is the powerful way to describe logic. Without needing to deal with React, Hooks, Rerenders, StrictMode, Next.js, etc.
pages/example/@id
Let's talk about data loading.
You can always use simple createEffect
to load data in Browser, just react on user actions, not pageStarted
nor appStarted
.
Until, you read Data Fetching article from Vike.dev. It will works until Client-Side Routing.
Vike has +data.ts
hook to fetch data on client and server navigation.
In case of refetch data using triggering some event on client side, or changing filters in user interface consider making client navigation with query parameters.
pages/example/@id/+data.ts
Declare your data fetcher. Name of the exported function must be data
. Use this as starting point:
import type { PageContextServer } from "vike/types";
export async function data(pageContext: PageContextServer) {
const { routeParams } = pageContext;
const { id } = routeParams;
// await api.someItems.getById(id)
return {
sampleData: { id: id ?? "<empty>" },
};
}
Consider placing all reusable API requests into src/shared/api
.
Using barrel file pattern is optional but very useful.
pages/example/@id/+pageStarted.ts
In case of data loading, hook pageStarted should be modified:
import { createPageStart } from "~/shared/init";
import type { data } from "./+data";
export const pageStarted = createPageStart<Awaited<ReturnType<typeof data>>>();
You need just bind resulting type of your data
loader function. Vike passes result of data()
call into pageStarted
like { params: { id }, data }
.
pages/example/@id/model.ts
So, you can access data from model
like this:
import { createStore, sample } from "effector";
import { pageStarted } from "./+pageStarted";
sample({
clock: pageStarted,
fn: ({ data, params: { id } }) => data,
target: insertHereAnyUnit,
});
// you can actually use `source, filter` in sample
pages/_error
Component created as pages/_error/+Page.tsx
is used to show error page.
renderer/
Here described exact integration of vike, react, and effector. You may need to read this before changing:
server/
server/config.ts
Contains resolvers of configuration variables.
export const CONFIG = {
get SERVER_PORT() {
return Number.parseInt(globalThis.process.env.SERVER_PORT ?? "3000", 10);
},
};
This is the only way it works on Cloudflare Workers. If you have a better solution please Leave an issue.
server/directory-root.ts
Declares project root directory, to resolve static assets from.
server/index.ts
Creates instance of server/server
, handles signals SIGINT, SIGTERM.
server/server.ts
Creates fastify instance, configures plugins, read cookies, renders using vike/server.
You can modify any part of this and any other files. Please, explore documentation before.
server/tsconfig.json
Used here to describe different environment for files in this directory.
It builds for production with tsc -p server
. It runs for development with ts-node
ESM-loader.
There is no hot reload in server/
directory. Restart manually after changing these files.
src/
Consider using Feature-Sliced Design structuring this directory.
src/vike.d.ts
Handles pageContext
typings.
src/vite-env.d.ts
Handles Vite client types.
What kind of customizations needs to be described? integration with supabase? Leave an issue.
First of all, delete pnpm-lock.yaml
, dist, and node_modules:
rm pnpm-lock.yaml
rm -rf node_modules dist
Set exact version into packageManager
field of package.json
:
// package.json
{
"packageManager": "[email protected]" // "[email protected]",
}
Save and navigate to your terminal into the project directory:
# Enable corepack for your shell
corepack enable
# Install package manager for your project
corepack prepare
# Install dependencies
npm install
Node.js v20 has different version of corepack, so we can use corepack use
.
# Enable corepack for your shell
corepack enable
# Install package manager for your project
corepack use npm@latest # yarn@3
# Install dependencies
npm install
# Check packageManager field in package.json
jq .packageManager package.json
#> "[email protected]+sha256.1ee0e26fb669143425371ab8727fe4c5841640a2fd944863a8e8c28be966aca2"
# It's OK
This template has been tested on:
We welcome contributions to the project! Please read our contribution guidelines before submitting a pull request.
This project is licensed under the MIT License - see the LICENSE file for details.