-
Notifications
You must be signed in to change notification settings - Fork 1k
feat[remix][gen2]: add remix gen2 snippets #3910
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
649e4e8
0dc2fca
c66f85c
827da34
72feefa
35ffcb2
a3a385d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.react-router | ||
build | ||
node_modules | ||
README.md |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.DS_Store | ||
/node_modules/ | ||
|
||
# React Router | ||
/.react-router/ | ||
/build/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
FROM node:20-alpine AS development-dependencies-env | ||
COPY . /app | ||
WORKDIR /app | ||
RUN npm ci | ||
|
||
FROM node:20-alpine AS production-dependencies-env | ||
COPY ./package.json package-lock.json /app/ | ||
WORKDIR /app | ||
RUN npm ci --omit=dev | ||
|
||
FROM node:20-alpine AS build-env | ||
COPY . /app/ | ||
COPY --from=development-dependencies-env /app/node_modules /app/node_modules | ||
WORKDIR /app | ||
RUN npm run build | ||
|
||
FROM node:20-alpine | ||
COPY ./package.json package-lock.json /app/ | ||
COPY --from=production-dependencies-env /app/node_modules /app/node_modules | ||
COPY --from=build-env /app/build /app/build | ||
WORKDIR /app | ||
CMD ["npm", "run", "start"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# Welcome to React Router! | ||
|
||
A modern, production-ready template for building full-stack React applications using React Router. | ||
|
||
[](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default) | ||
|
||
## Features | ||
|
||
- 🚀 Server-side rendering | ||
- ⚡️ Hot Module Replacement (HMR) | ||
- 📦 Asset bundling and optimization | ||
- 🔄 Data loading and mutations | ||
- 🔒 TypeScript by default | ||
- 🎉 TailwindCSS for styling | ||
- 📖 [React Router docs](https://reactrouter.com/) | ||
|
||
## Getting Started | ||
|
||
### Installation | ||
|
||
Install the dependencies: | ||
|
||
```bash | ||
npm install | ||
``` | ||
|
||
### Development | ||
|
||
Start the development server with HMR: | ||
|
||
```bash | ||
npm run dev | ||
``` | ||
|
||
Your application will be available at `http://localhost:5173`. | ||
|
||
## Building for Production | ||
|
||
Create a production build: | ||
|
||
```bash | ||
npm run build | ||
``` | ||
|
||
## Deployment | ||
|
||
### Docker Deployment | ||
|
||
This template includes three Dockerfiles optimized for different package managers: | ||
|
||
- `Dockerfile` - for npm | ||
- `Dockerfile.pnpm` - for pnpm | ||
- `Dockerfile.bun` - for bun | ||
|
||
To build and run using Docker: | ||
|
||
```bash | ||
# For npm | ||
docker build -t my-app . | ||
|
||
# For pnpm | ||
docker build -f Dockerfile.pnpm -t my-app . | ||
|
||
# For bun | ||
docker build -f Dockerfile.bun -t my-app . | ||
|
||
# Run the container | ||
docker run -p 3000:3000 my-app | ||
``` | ||
|
||
The containerized application can be deployed to any platform that supports Docker, including: | ||
|
||
- AWS ECS | ||
- Google Cloud Run | ||
- Azure Container Apps | ||
- Digital Ocean App Platform | ||
- Fly.io | ||
- Railway | ||
|
||
### DIY Deployment | ||
|
||
If you're familiar with deploying Node applications, the built-in app server is production-ready. | ||
|
||
Make sure to deploy the output of `npm run build` | ||
|
||
``` | ||
├── package.json | ||
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb) | ||
├── build/ | ||
│ ├── client/ # Static assets | ||
│ └── server/ # Server-side code | ||
``` | ||
|
||
## Styling | ||
|
||
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer. | ||
|
||
--- | ||
|
||
Built with ❤️ using React Router. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
@import "tailwindcss"; | ||
|
||
@theme { | ||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, | ||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | ||
} | ||
|
||
html, | ||
body { | ||
@apply bg-white dark:bg-gray-950; | ||
|
||
@media (prefers-color-scheme: dark) { | ||
color-scheme: dark; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { | ||
isRouteErrorResponse, | ||
Links, | ||
Meta, | ||
Outlet, | ||
Scripts, | ||
ScrollRestoration, | ||
} from "react-router"; | ||
|
||
import type { Route } from "./+types/root"; | ||
import "./app.css"; | ||
|
||
export const links: Route.LinksFunction = () => [ | ||
{ rel: "preconnect", href: "https://fonts.googleapis.com" }, | ||
{ | ||
rel: "preconnect", | ||
href: "https://fonts.gstatic.com", | ||
crossOrigin: "anonymous", | ||
}, | ||
{ | ||
rel: "stylesheet", | ||
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", | ||
}, | ||
]; | ||
|
||
export function Layout({ children }: { children: React.ReactNode }) { | ||
return ( | ||
<html lang="en"> | ||
<head> | ||
<meta charSet="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<Meta /> | ||
<Links /> | ||
</head> | ||
<body> | ||
{children} | ||
<ScrollRestoration /> | ||
<Scripts /> | ||
</body> | ||
</html> | ||
); | ||
} | ||
|
||
export default function App() { | ||
return <Outlet />; | ||
} | ||
|
||
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { | ||
let message = "Oops!"; | ||
let details = "An unexpected error occurred."; | ||
let stack: string | undefined; | ||
|
||
if (isRouteErrorResponse(error)) { | ||
message = error.status === 404 ? "404" : "Error"; | ||
details = | ||
error.status === 404 | ||
? "The requested page could not be found." | ||
: error.statusText || details; | ||
} else if (import.meta.env.DEV && error && error instanceof Error) { | ||
details = error.message; | ||
stack = error.stack; | ||
} | ||
|
||
return ( | ||
<main className="pt-16 p-4 container mx-auto"> | ||
<h1>{message}</h1> | ||
<p>{details}</p> | ||
{stack && ( | ||
<pre className="w-full p-4 overflow-x-auto"> | ||
<code>{stack}</code> | ||
</pre> | ||
)} | ||
</main> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { type RouteConfig, index } from "@react-router/dev/routes"; | ||
|
||
export default [index("routes/builder-page.tsx")] satisfies RouteConfig; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import React from 'react'; | ||
import { | ||
Content, | ||
fetchOneEntry, | ||
getBuilderSearchParams, | ||
isPreviewing, | ||
type BuilderContent, | ||
} from '@builder.io/sdk-react'; | ||
|
||
// Builder Public API Key set in .env file | ||
const BUILDER_API_KEY = 'ee9f13b4981e489a9a1209887695ef2b'; | ||
const MODEL_NAME = 'page'; | ||
|
||
export default function BuilderPage() { | ||
const [notFound, setNotFound] = React.useState(false); | ||
const [content, setContent] = React.useState<BuilderContent | null>(null); | ||
|
||
// get the page content from Builder | ||
React.useEffect(() => { | ||
fetchOneEntry({ | ||
model: MODEL_NAME, | ||
apiKey: BUILDER_API_KEY, | ||
userAttributes: { | ||
urlPath: window.location.pathname, | ||
}, | ||
options: getBuilderSearchParams(new URL(location.href).searchParams), | ||
}) | ||
.then((content) => { | ||
if (content) { | ||
setContent(content); | ||
} | ||
setNotFound(!content); | ||
}) | ||
.catch((err) => { | ||
console.log('Oops: ', err); | ||
}); | ||
}, []); | ||
|
||
// If no page is found, return | ||
// a 404 page from your code. | ||
if (notFound && !isPreviewing()) { | ||
return <div>404 Page Not Found</div>; | ||
} | ||
|
||
// return the page when found | ||
return ( | ||
<> | ||
{/* Render the Builder page */} | ||
<Content | ||
content={content} | ||
model={MODEL_NAME} | ||
apiKey={BUILDER_API_KEY} | ||
/> | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||||
{ | ||||||||
"name": "@snippet/remix", | ||||||||
"private": true, | ||||||||
"version": "0.0.0", | ||||||||
"type": "module", | ||||||||
"scripts": { | ||||||||
"build": "react-router build", | ||||||||
"dev": "react-router dev", | ||||||||
"start": "react-router-serve ./build/server/index.js", | ||||||||
"typecheck": "react-router typegen && tsc", | ||||||||
"test": "SERVER_NAME=remix yarn g:nx test:snippet @sdk/tests" | ||||||||
}, | ||||||||
"dependencies": { | ||||||||
"@builder.io/sdk-react": "workspace:*", | ||||||||
"@react-router/node": "^7.1.5", | ||||||||
"@react-router/serve": "^7.1.5", | ||||||||
"isbot": "^5.1.17", | ||||||||
"react": "^19.0.0", | ||||||||
"react-dom": "^19.0.0", | ||||||||
"react-router": "^7.1.5" | ||||||||
}, | ||||||||
"devDependencies": { | ||||||||
"@react-router/dev": "^7.1.5", | ||||||||
"@tailwindcss/vite": "^4.0.0", | ||||||||
"@types/node": "^20", | ||||||||
"@types/react": "^19.0.1", | ||||||||
"@types/react-dom": "^19.0.1", | ||||||||
"react-router-devtools": "^1.1.0", | ||||||||
"tailwindcss": "^4.0.0", | ||||||||
"typescript": "^5.7.2", | ||||||||
"vite": "^5.4.11", | ||||||||
"vite-tsconfig-paths": "^5.1.4" | ||||||||
}, | ||||||||
"installConfig": { | ||||||||
"hoistingLimits": "workspaces" | ||||||||
}, | ||||||||
"nx": { | ||||||||
"targets": { | ||||||||
"build": { | ||||||||
"outputs": [ | ||||||||
"{projectRoot}/build/client", | ||||||||
"{projectRoot}/build/server" | ||||||||
] | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this PR is breaking all tests relying on React. Probably because this snippet installs a new react version at the root of the monorepo, causing other tests to use it when they should stick to a different version. See how we configure other remix snippets to scope packages locally: builder/packages/sdks/snippets/gen1-remix/package.json Lines 41 to 43 in 38b6b86
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { Config } from "@react-router/dev/config"; | ||
|
||
export default { | ||
// Config options... | ||
// Server-side render by default, to enable SPA mode set this to `false` | ||
ssr: true, | ||
} satisfies Config; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"include": [ | ||
"**/*", | ||
"**/.server/**/*", | ||
"**/.client/**/*", | ||
".react-router/types/**/*" | ||
], | ||
"compilerOptions": { | ||
"lib": ["DOM", "DOM.Iterable", "ES2022"], | ||
"types": ["node", "vite/client"], | ||
"target": "ES2022", | ||
"module": "ES2022", | ||
"moduleResolution": "bundler", | ||
"jsx": "react-jsx", | ||
"rootDirs": [".", "./.react-router/types"], | ||
"baseUrl": ".", | ||
"paths": { | ||
"~/*": ["./app/*"] | ||
}, | ||
"esModuleInterop": true, | ||
"verbatimModuleSyntax": true, | ||
"noEmit": true, | ||
"resolveJsonModule": true, | ||
"skipLibCheck": true, | ||
"strict": true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { reactRouter } from "@react-router/dev/vite"; | ||
import tailwindcss from "@tailwindcss/vite"; | ||
import { defineConfig } from "vite"; | ||
import tsconfigPaths from "vite-tsconfig-paths"; | ||
|
||
export default defineConfig({ | ||
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need a
serve
command for tests to run