Skip to content

Commit c72d034

Browse files
committed
Added seo handling
1 parent c8ade33 commit c72d034

21 files changed

Lines changed: 9188 additions & 13511 deletions

.env.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
# Add your env variables here
1+
# Add your env variables here
2+
DEPLOYMENT_ENV="staging"

.vscode/settings.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,14 @@
2929
"[javascript][typescript][typescriptreact][javascriptreact][json][jsonc][vue][astro][svelte][css][graphql]": {
3030
"editor.defaultFormatter": "biomejs.biome"
3131
},
32-
32+
"explorer.fileNesting.patterns": {
33+
"*.ts": "${basename}.*.${extname}",
34+
".env": ".env.*",
35+
"*.tsx": "${basename}.*.${extname},${basename}.*.ts",
36+
"package.json": "*.json, *.yml, *.config.js, *.config.ts, *.yaml",
37+
"readme*": "AUTHORS, Authors, BACKERS*, Backers*, CHANGELOG*, CITATION*, CODEOWNERS, CODE_OF_CONDUCT*, CONTRIBUTING*, CONTRIBUTORS, COPYING*, CREDITS, Changelog*, Citation*, Code_Of_Conduct*, Codeowners, Contributing*, Contributors, Copying*, Credits, GOVERNANCE.MD, Governance.md, HISTORY.MD, History.md, LICENSE*, License*, MAINTAINERS, Maintainers, README-*, README_*, RELEASE_NOTES*, ROADMAP.MD, Readme-*, Readme_*, Release_Notes*, Roadmap.md, SECURITY.MD, SPONSORS*, Security.md, Sponsors*, authors, backers*, changelog*, citation*, code_of_conduct*, codeowners, contributing*, contributors, copying*, credits, governance.md, history.md, license*, maintainers, readme-*, readme_*, release_notes*, roadmap.md, security.md, sponsors*",
38+
"Readme*": "AUTHORS, Authors, BACKERS*, Backers*, CHANGELOG*, CITATION*, CODEOWNERS, CODE_OF_CONDUCT*, CONTRIBUTING*, CONTRIBUTORS, COPYING*, CREDITS, Changelog*, Citation*, Code_Of_Conduct*, Codeowners, Contributing*, Contributors, Copying*, Credits, GOVERNANCE.MD, Governance.md, HISTORY.MD, History.md, LICENSE*, License*, MAINTAINERS, Maintainers, README-*, README_*, RELEASE_NOTES*, ROADMAP.MD, Readme-*, Readme_*, Release_Notes*, Roadmap.md, SECURITY.MD, SPONSORS*, Security.md, Sponsors*, authors, backers*, changelog*, citation*, code_of_conduct*, codeowners, contributing*, contributors, copying*, credits, governance.md, history.md, license*, maintainers, readme-*, readme_*, release_notes*, roadmap.md, security.md, sponsors*",
39+
"README*": "AUTHORS, Authors, BACKERS*, Backers*, CHANGELOG*, CITATION*, CODEOWNERS, CODE_OF_CONDUCT*, CONTRIBUTING*, CONTRIBUTORS, COPYING*, CREDITS, Changelog*, Citation*, Code_Of_Conduct*, Codeowners, Contributing*, Contributors, Copying*, Credits, GOVERNANCE.MD, Governance.md, HISTORY.MD, History.md, LICENSE*, License*, MAINTAINERS, Maintainers, README-*, README_*, RELEASE_NOTES*, ROADMAP.MD, Readme-*, Readme_*, Release_Notes*, Roadmap.md, SECURITY.MD, SPONSORS*, Security.md, Sponsors*, authors, backers*, changelog*, citation*, code_of_conduct*, codeowners, contributing*, contributors, copying*, credits, governance.md, history.md, license*, maintainers, readme-*, readme_*, release_notes*, roadmap.md, security.md, sponsors*",
40+
"Dockerfile": "*.dockerfile, .devcontainer.*, .dockerignore, captain-definition, compose.*, docker-compose.*, dockerfile*"
41+
}
3342
}

README.md

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ It includes a basic setup for a project with Remix.run and:
2121
- lefthook hooks
2222
- CI checks for quality control
2323
- remix-development-tools
24+
- Hono server
25+
- .env var handling for server and client
26+
- SEO robots.txt, sitemap-index and sitemap built in.
2427

2528
## Internationalization
2629

@@ -32,21 +35,44 @@ Features included out of the box:
3235
- language switcher
3336
- language detector (uses the request to detect the language, falls back to your fallback language)
3437

35-
## How to use
38+
## Hono server
3639

37-
1. Initialize the repository with our CLI:
38-
```bash
39-
npx f42 init -t base-stack -o ./your-project-name-here
40+
This stack uses Hono for the server. More information about Hono can be found [here](https://honojs.dev/).
41+
Another important thing to note is that we use a dependency called `react-router-hono-server` which is a wrapper for Hono that allows us to use Hono in our React Router application.
42+
43+
The server comes preconfigured with:
44+
- i18next middleware
45+
- caching middleware for assets
46+
- easily extendable global application context
47+
- .env injection into context
48+
49+
In order to add your own middleware, extend the context, or anything along those lines, all you have to do is edit the server
50+
inside the `entry.server.tsx` file.
51+
52+
## .env handling
53+
54+
This stack parses your `.env` file and injects it into the server context. For the client side, in the `root.tsx` file, we use the `useLoaderData` hook to get the `clientEnv` from the server and set it as a global variable on the `window` called `env`.
55+
If you need to access the env variables in both environments, you can create a polyEnv helper like this:
56+
```ts
57+
// app/utils/env.ts
58+
// This will return the process.env on the server and window.env on the client
59+
export const polyEnv = typeof process !== "undefined" ? process.env : window.env;
4060
```
61+
The server will fail at runtime if you don't set your `.env` file properly.
62+
63+
## Getting started
64+
65+
1. Fork the repository
66+
4167
2. Install the dependencies:
4268
```bash
43-
npm install
69+
pnpm install
4470
```
4571
3. Read through the README.md files in the project to understand our decisions.
4672

4773
4. Run the cleanup script:
4874
```bash
49-
npm run cleanup
75+
pnpm cleanup
5076
```
5177

5278
This will remove everything in the project related to the base-stack like README.md etc.

app/entry.server.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1-
21
import { PassThrough } from "node:stream"
32
import type { AppLoadContext, EntryContext } from "@remix-run/node"
43
import { RemixServer } from "@remix-run/react"
54
import type { Context } from "hono"
65
import { createInstance } from "i18next"
7-
import Backend from "i18next-fs-backend"
86
import { isbot } from "isbot"
97
import { renderToPipeableStream } from "react-dom/server"
108
import { I18nextProvider, initReactI18next } from "react-i18next"
119
import { createHonoServer } from "react-router-hono-server/node"
1210
import { i18next } from "remix-hono/i18next"
11+
import { getClientEnv, initEnv } from "./env.server"
1312
import i18n from "./localization/i18n" // your i18n configuration file
1413
import i18nextOpts from "./localization/i18n.server"
1514
import { resources } from "./localization/resource"
16-
import { getClientEnv, initEnv } from "./server/env.server"
1715

1816
const ABORT_DELAY = 5000
1917

@@ -26,12 +24,11 @@ export default async function handleRequest(
2624
) {
2725
const callbackName = isbot(request.headers.get("user-agent")) ? "onAllReady" : "onShellReady"
2826
const instance = createInstance()
29-
const lng = appContext.lang;
27+
const lng = appContext.lang
3028
const ns = i18nextOpts.getRouteNamespaces(remixContext)
3129

3230
await instance
3331
.use(initReactI18next) // Tell our instance to use react-i18next
34-
.use(Backend) // Setup our backend
3532
.init({
3633
...i18n, // spread the configuration
3734
lng, // The locale we detected above
@@ -53,7 +50,7 @@ export default async function handleRequest(
5350
responseHeaders.set("Content-Type", "text/html")
5451

5552
resolve(
56-
// @ts-expect-error - We purposely do not define the body as existent so it's not used inside loaders as it's injected there as well
53+
// @ts-expect-error - We purposely do not define the body as existent so it's not used inside loaders as it's injected there as well
5754
appContext.body(body, {
5855
headers: responseHeaders,
5956
status: didError ? 500 : responseStatusCode,
@@ -77,7 +74,6 @@ export default async function handleRequest(
7774
})
7875
}
7976

80-
8177
// Code below used to initialize our own Hono server!
8278
// Setup the .env vars
8379
const env = initEnv()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { z } from "zod"
22

33
const envSchema = z.object({
44
NODE_ENV: z.enum(["development", "production", "test"]),
5-
5+
DEPLOYMENT_ENV: z.enum(["staging", "production"]),
66
})
77

88
type APP_ENV = z.infer<typeof envSchema>

app/library/icon/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Icon generation and spritesheets
22

3-
This directory is the output directory for the icons. The icons are generated from the `resources/icons` directory.
3+
This directory is the output directory for the icons. The icons are generated from the `resources/icons` directory.
44

5-
The icons are generated using the `scripts/icons.ts` script.
5+
The icons are generated using the `vite-plugin-icons-spritesheet` package.
66

7-
All the icons are generated as symbols inside of a spritesheet svg element and the `Icon.tsx`
7+
All the icons are generated as symbols inside of a spritesheet svg element and the `Icon.tsx`
88
component uses the spritesheet to display the icons.
99

1010
The `Icon.tsx` component is a simple component that takes a `name` prop and displays the icon. It is fully

app/library/icon/icons/types.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// This file is generated by icon spritesheet generator
22

3-
export const iconNames = [
4-
"ShoppingCart",
5-
] as const
3+
export const iconNames = ["ShoppingCart"] as const
64

7-
export type IconName = typeof iconNames[number]
5+
export type IconName = (typeof iconNames)[number]

app/localization/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Localization
2+
3+
Localization works by using the `i18next` package. Everything is configured inside of this folder.
4+
The localization works by using the `/resources/locales` folder. This folder contains all the translations for the different languages. You can add new translations by adding new files to this folder and then changing the `resources.ts` file to include the new language.
5+
6+
The server part is set up in the `entry.server.tsx` file, and the client part, conversely, is in the `entry.client.tsx` file and also the `root.tsx` file.
7+
8+
The language is changed by setting the `lng` search parameter in the url.
9+
10+
## Server-side
11+
12+
Due to the fact that the server does not care about loading in additional resources as they are not send over the wire we
13+
pass in `resources` to the `i18next` instance. This provides all the languages to your server which allows it to render
14+
the correct language on the server.
15+
16+
## Client-side
17+
18+
The client-side is a bit more complicated. We do not want to load in all the languages on the client side as it would
19+
be a lot of requests. Instead, we use the fetch backend to load in the language files on the client side. We have a resource route inside of the `routes` directory which is in charge of loading in the resources. This route is called `resource.locales` and it is used to load in the languages. The `resource.locales` route is set up to only load in the languages and namespaces that are needed. In production we cache these responses and in development we don't cache them.

app/localization/resource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import english from "../../resources/locales/en/common.json"
33

44
const languages = ["en", "bs"] as const
55
export const supportedLanguages = [...languages]
6-
export type Language = (typeof languages)[number]
6+
type Language = (typeof languages)[number]
77

88
type Resource = {
99
common: typeof english

app/root.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useChangeLanguage } from "remix-i18next/react"
66
import { LanguageSwitcher } from "./library/language-switcher"
77
import tailwindcss from "./tailwind.css?url"
88

9-
export async function loader({ context: { lang, clientEnv } }: LoaderFunctionArgs) {
9+
export async function loader({ context: { lang, clientEnv } }: LoaderFunctionArgs) {
1010
return json({ lang, clientEnv })
1111
}
1212

0 commit comments

Comments
 (0)