├── .editorconfig ├── .github └── workflows │ ├── check-sdk.yml │ ├── check-starters.yml │ ├── release-starters.yml │ └── smoke-test-starters.yml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── SMOKE-TESTING.md ├── docs ├── .eslintignore ├── .gitignore ├── .node-version ├── .prettierignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── settings.json ├── README.md ├── TODO.md ├── astro.config.mjs ├── ec.config.mjs ├── package.json ├── public │ └── favicon.svg ├── src │ ├── assets │ │ ├── dark-logo.svg │ │ ├── favicon.ico │ │ ├── light-logo.svg │ │ └── og-docs.png │ ├── content.config.ts │ ├── content │ │ └── docs │ │ │ ├── core │ │ │ ├── authentication.mdx │ │ │ ├── cron.mdx │ │ │ ├── database.mdx │ │ │ ├── env-vars.mdx │ │ │ ├── hosting.mdx │ │ │ ├── images │ │ │ │ ├── active-domain-email-confirmation.png │ │ │ │ ├── add-a-domain.png │ │ │ │ ├── check-nameservers.png │ │ │ │ ├── checking-nameservers.png │ │ │ │ ├── cloudflare-cron-events.png │ │ │ │ ├── cloudflare-ui-for-env.png │ │ │ │ ├── enter-existing-domain.png │ │ │ │ ├── env-in-cursor-sidebar.png │ │ │ │ ├── last-step-update-ns.png │ │ │ │ ├── review-dns.png │ │ │ │ ├── search-for-domain-cloudflare.png │ │ │ │ ├── select-plan.png │ │ │ │ ├── trigger-events-view-events.png │ │ │ │ └── trigger-events.png │ │ │ ├── overview.mdx │ │ │ ├── queues.mdx │ │ │ ├── react-server-components.mdx │ │ │ ├── realtime.mdx │ │ │ ├── routing.mdx │ │ │ ├── security.mdx │ │ │ └── storage.mdx │ │ │ ├── getting-started │ │ │ ├── first-project.mdx │ │ │ ├── images │ │ │ │ ├── hello-world.png │ │ │ │ └── workers-pages.png │ │ │ └── quick-start.mdx │ │ │ ├── guides │ │ │ ├── email │ │ │ │ ├── 1-sending-email.mdx │ │ │ │ ├── 2-email-templates.mdx │ │ │ │ └── images │ │ │ │ │ ├── 1-resend-homepage.png │ │ │ │ │ ├── 2-resend-create-account.png │ │ │ │ │ ├── 3-resend-send-first-email.png │ │ │ │ │ ├── 4-resend-create-api-key.png │ │ │ │ │ ├── installed-react-email.png │ │ │ │ │ ├── react-email-dev.png │ │ │ │ │ ├── react-email-get-code.png │ │ │ │ │ ├── react-email-preview-in-browser.png │ │ │ │ │ └── react-email-template-files.png │ │ │ ├── frontend │ │ │ │ ├── documents.mdx │ │ │ │ ├── images │ │ │ │ │ ├── og-html-example.png │ │ │ │ │ ├── og-react-example.png │ │ │ │ │ ├── opengraph-xyz.png │ │ │ │ │ ├── sb-client-component-console-error.png │ │ │ │ │ ├── sb-ctx-argtypes.gif │ │ │ │ │ ├── sb-home-story-1.png │ │ │ │ │ ├── sb-home-story-2.png │ │ │ │ │ ├── sb-mocked-user-list.png │ │ │ │ │ ├── sb-prisma-error.png │ │ │ │ │ ├── sb-select-react.png │ │ │ │ │ ├── sb-select-use.png │ │ │ │ │ ├── sb-served-site.png │ │ │ │ │ └── sb-started.png │ │ │ │ ├── layouts.mdx │ │ │ │ ├── metadata.mdx │ │ │ │ ├── og-images.mdx │ │ │ │ ├── public-assets.mdx │ │ │ │ ├── shadcn.mdx │ │ │ │ ├── storybook.mdx │ │ │ │ └── tailwind.mdx │ │ │ └── rsc-streams.mdx │ │ │ ├── index.mdx │ │ │ ├── reference │ │ │ ├── create-rwsdk.mdx │ │ │ ├── sdk-router.mdx │ │ │ └── sdk-worker.mdx │ │ │ └── tutorial │ │ │ └── full-stack-app │ │ │ ├── 1-setup.mdx │ │ │ ├── 2-create-app.mdx │ │ │ ├── 3-database-setup.mdx │ │ │ ├── 4-auth.mdx │ │ │ ├── 5-jobs-list.mdx │ │ │ ├── 6-jobs-form.mdx │ │ │ ├── 7-contacts.mdx │ │ │ ├── 8-jobs-details.mdx │ │ │ ├── 9-deploying.mdx │ │ │ └── images │ │ │ ├── add-contact.png │ │ │ ├── after-layout-component.png │ │ │ ├── alert-message-successful-login.png │ │ │ ├── all-applications-heading-styled.png │ │ │ ├── application-detail-header-section.png │ │ │ ├── application-detail-no-company-details.png │ │ │ ├── application-detail-two-columns.png │ │ │ ├── application-detail-with-interior-layout.png │ │ │ ├── application-edit-details.png │ │ │ ├── application-form-right-side-boxes.png │ │ │ ├── application-list-table-preview.png │ │ │ ├── application-list.png │ │ │ ├── application-seed-file-related-contact.png │ │ │ ├── application-seed-linting-error.png │ │ │ ├── application-status-does-not-exist-on-type.png │ │ │ ├── application-status-in-db.png │ │ │ ├── application-status-in-prisma-studio.png │ │ │ ├── application-status-type-error.png │ │ │ ├── applications-id-link-error.png │ │ │ ├── applications-json-with-interior-layout.png │ │ │ ├── applications-list-json.png │ │ │ ├── applications-list-view-icon.png │ │ │ ├── applications-list-view-source.png │ │ │ ├── applications-prop-type-error.png │ │ │ ├── associated-contact-in-applications-table.png │ │ │ ├── authlayout-children-typescript-error.png │ │ │ ├── authlayout-left-column-styling-finished.png │ │ │ ├── authlayout-styling-complete.png │ │ │ ├── before-layout-component.png │ │ │ ├── blank-signup-page.png │ │ │ ├── browser-application-status-expanded.png │ │ │ ├── button-needs-use-client-invalid-hook-call.png │ │ │ ├── button-primary-color.png │ │ │ ├── button-variant-error.png │ │ │ ├── calendar-icon-filled.png │ │ │ ├── calendar-icon-stroke.png │ │ │ ├── center-column-content-with-utility-class.png │ │ │ ├── cloudflare-confirm-project-delete.png │ │ │ ├── cloudflare-delete-project.png │ │ │ ├── cloudflare-once-connected-to-github.png │ │ │ ├── cloudflare-visit-website.png │ │ │ ├── connect-build-to-github.png │ │ │ ├── connecting-to-custom-domain.png │ │ │ ├── contact-box-with-delete-button.png │ │ │ ├── contact-created-successfully-console.png │ │ │ ├── contact-form-linting-error.png │ │ │ ├── contact-sheet-preview.png │ │ │ ├── contact-sidebar-with-trigger-preview.png │ │ │ ├── contacts-list-with-temporary-display.png │ │ │ ├── create-application.png │ │ │ ├── database-url-full-url.png │ │ │ ├── database.png │ │ │ ├── datepicker-linting-error.png │ │ │ ├── db-application-to-company.png │ │ │ ├── db-application-to-status.png │ │ │ ├── db-contact-to-company.png │ │ │ ├── default-value-does-not-exist-date-picker.png │ │ │ ├── define-link-recommendations.png │ │ │ ├── dialogue-trigger-nested-buttons.png │ │ │ ├── different-data-types-prisma-studio.png │ │ │ ├── different-data-types-sqlite-viewer.png │ │ │ ├── edit-job-application-breadcrumbs.png │ │ │ ├── empty-application-detail-page.png │ │ │ ├── empty-contacts-section.png │ │ │ ├── error-deleting-contact.png │ │ │ ├── error-running-new-migration.png │ │ │ ├── existing-domain-name-in-cloudflare.png │ │ │ ├── exporting-icons-out-of-figma.png │ │ │ ├── figma-add-a-contact-side-panel.png │ │ │ ├── figma-add-contact-button.png │ │ │ ├── figma-application-list.png │ │ │ ├── figma-header-mockup.png │ │ │ ├── figma-job-details-page.png │ │ │ ├── figma-login.png │ │ │ ├── figma-new-application-form-right-side.png │ │ │ ├── figma-new-application-form.png │ │ │ ├── figma-salary-min-max.png │ │ │ ├── final-applications-table.png │ │ │ ├── final-login-page-styles.png │ │ │ ├── final-signup-page.png │ │ │ ├── finished-interior-layout.png │ │ │ ├── github-advanced-settings-root-directory.png │ │ │ ├── github-config.png │ │ │ ├── github-details-after-checks-passed.png │ │ │ ├── github-view-after-new-commit.png │ │ │ ├── github-view-all-checks-passed-alt.png │ │ │ ├── github-view-all-checks-passed-checkbox.png │ │ │ ├── github-view-all-checks-passed-details.png │ │ │ ├── google-fonts-embed-code.png │ │ │ ├── google-fonts-font-family.png │ │ │ ├── google-fonts-get-fonts.png │ │ │ ├── google-fonts-selected-fonts.png │ │ │ ├── header-logo-zoomed-in.png │ │ │ ├── home-page-logged-in-as.png │ │ │ ├── icons-within-figma.png │ │ │ ├── individual-application-placeholder.png │ │ │ ├── initial-look.png │ │ │ ├── json-with-relationships.png │ │ │ ├── lemon-lime-svgs.png │ │ │ ├── lib-utils-directory-error.png │ │ │ ├── linting-error-cannot-find-contact.png │ │ │ ├── local-wrangler-d1-database.png │ │ │ ├── login-page-text-styled.png │ │ │ ├── login-with-passkey.png │ │ │ ├── many-to-many.png │ │ │ ├── migrations-folder.png │ │ │ ├── new-application-form-styled-left-side.png │ │ │ ├── new-application-header.png │ │ │ ├── new-contact-in-prisma-studio.png │ │ │ ├── null-reading-use-state.png │ │ │ ├── one-to-many.png │ │ │ ├── one-to-one.png │ │ │ ├── openssl-rand-base64.png │ │ │ ├── params-in-terminal.png │ │ │ ├── passkey-form-before-tailwind.png │ │ │ ├── passkey-starter-tailwind-working.png │ │ │ ├── pnpm-migrate-dev-contacts-users-relationship.png │ │ │ ├── preview-applications-json-stringify.png │ │ │ ├── preview-applications-table.png │ │ │ ├── preview-quote-on-auth.png │ │ │ ├── preview-quote-relative-to-container.png │ │ │ ├── preview-two-column-auth-layout.png │ │ │ ├── prisma-relationship-field.png │ │ │ ├── prisma-studio-application-table-selected.png │ │ │ ├── prisma-studio-no-tables-selected.png │ │ │ ├── prisma-studio.png │ │ │ ├── project-live-custom-domain.png │ │ │ ├── red-black-x-contact-delete-button.png │ │ │ ├── register-with-passkey.png │ │ │ ├── related-credential-in-database.png │ │ │ ├── seed-database-in-terminal.png │ │ │ ├── setup-all-tables-migration.png │ │ │ ├── setup-lemon-lime-svgs.png │ │ │ ├── shad-cn-components-in-finder.png │ │ │ ├── shadcn-badge.png │ │ │ ├── shadcn-neutral-theme.png │ │ │ ├── shadcn-sheet-api-docs.png │ │ │ ├── shadcn-table-component.png │ │ │ ├── shadcn-working.png │ │ │ ├── side-panel-custom-domain.png │ │ │ ├── signup.png │ │ │ ├── stubbed-out-applications-list-page.png │ │ │ ├── styled-add-a-contact-button.png │ │ │ ├── styled-add-a-contact-sheet.png │ │ │ ├── styled-contact-card.png │ │ │ ├── success-toast-message.png │ │ │ ├── successful-login.png │ │ │ ├── successful-registration.png │ │ │ ├── successfully-delete-contact.png │ │ │ ├── svg-fill-black.png │ │ │ ├── svg-icons-in-finder.png │ │ │ ├── table-body-static-content.png │ │ │ ├── tailwind-working.png │ │ │ ├── temporary-new-applications-page.png │ │ │ ├── terminal-install-starter.png │ │ │ ├── terminal-pnpm-icons.png │ │ │ ├── terminal-shadcn-bulk-add-result.png │ │ │ ├── terminal-shadcn-bulk-add.png │ │ │ ├── testing-custom-tailwind-colors.png │ │ │ ├── turnstile-add-widget-form.png │ │ │ ├── turnstile-add-widget.png │ │ │ ├── turnstile-site-key.png │ │ │ ├── unique-application-in-terminal.png │ │ │ ├── unstyled-new-application-form.png │ │ │ ├── unused-login-functions.png │ │ │ ├── user-id-in-database.png │ │ │ ├── user-in-database.png │ │ │ ├── view-icon-in-browser.png │ │ │ ├── vs-code-sqlite.png │ │ │ ├── working-job-application-form.png │ │ │ ├── working-terms-page.png │ │ │ ├── wrangler-d1-create.png │ │ │ ├── wrangler-rp-id.png │ │ │ ├── wrangler-secret-key.png │ │ │ ├── wrangler-turnstile-secret-key.png │ │ │ └── wrap-column-content-with-div.png │ └── styles │ │ └── custom.css └── tsconfig.json ├── examples └── README.md ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts └── check-app.sh ├── sdk ├── README.md ├── SMOKE-TESTING.md ├── bin │ └── rw-scripts.mjs ├── package.json ├── pnpm-lock.yaml ├── scripts │ └── release.sh ├── src │ ├── lib │ │ ├── $.mts │ │ ├── constants.mts │ │ ├── findWranglerConfig.mts │ │ ├── getShortName.mts │ │ ├── getSrcPaths.ts │ │ ├── hasPkgScript.mts │ │ ├── jsonUtils.mts │ │ ├── setupEnvFiles.mts │ │ └── smokeTests │ │ │ ├── artifacts.mts │ │ │ ├── browser.mts │ │ │ ├── cleanup.mts │ │ │ ├── codeUpdates.mts │ │ │ ├── constants.mts │ │ │ ├── development.mts │ │ │ ├── environment.mts │ │ │ ├── release.mts │ │ │ ├── reporting.mts │ │ │ ├── runSmokeTests.mjs │ │ │ ├── runSmokeTests.mts │ │ │ ├── state.mts │ │ │ ├── templates │ │ │ ├── SmokeTest.template.tsx │ │ │ ├── SmokeTestClient.template.tsx │ │ │ └── smokeTestFunctions.template.ts │ │ │ ├── types.mts │ │ │ └── utils.mts │ ├── llms │ │ ├── index.ts │ │ └── rules │ │ │ ├── interruptors.ts │ │ │ ├── middleware.ts │ │ │ ├── react.ts │ │ │ └── request-response.ts │ ├── runtime │ │ ├── client.tsx │ │ ├── constants.ts │ │ ├── entries │ │ │ ├── auth.ts │ │ │ ├── client.ts │ │ │ ├── clientSSR.ts │ │ │ ├── no-react-server.ts │ │ │ ├── react-server-only.ts │ │ │ ├── router.ts │ │ │ ├── ssr.ts │ │ │ └── worker.ts │ │ ├── error.ts │ │ ├── imports │ │ │ ├── client.ts │ │ │ ├── ssr.ts │ │ │ └── worker.ts │ │ ├── lib │ │ │ ├── auth │ │ │ │ ├── index.ts │ │ │ │ └── session.ts │ │ │ ├── links.ts │ │ │ ├── realtime │ │ │ │ ├── client.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── durableObject.ts │ │ │ │ ├── renderRealtimeClients.ts │ │ │ │ ├── shared.ts │ │ │ │ ├── validateUpgradeRequest.ts │ │ │ │ └── worker.ts │ │ │ ├── router.test.ts │ │ │ ├── router.ts │ │ │ ├── streams │ │ │ │ └── consumeEventStream.ts │ │ │ ├── turnstile │ │ │ │ ├── TurnstileScript.tsx │ │ │ │ ├── turnstile.ts │ │ │ │ ├── useTurnstile.ts │ │ │ │ └── verifyTurnstileToken.ts │ │ │ └── utils.ts │ │ ├── register │ │ │ ├── client.ts │ │ │ ├── ssr.ts │ │ │ └── worker.ts │ │ ├── render │ │ │ ├── createClientManifest.ts │ │ │ ├── createModuleMap.ts │ │ │ ├── renderRscThenableToHtmlStream.tsx │ │ │ ├── renderToRscStream.ts │ │ │ └── transformRscToHtmlStream.tsx │ │ ├── requestInfo │ │ │ ├── types.ts │ │ │ └── worker.ts │ │ ├── script.ts │ │ ├── ssrBridge.ts │ │ └── worker.tsx │ ├── scripts │ │ ├── __sdk.mts │ │ ├── debug-sync.mts │ │ ├── dev-init.mts │ │ ├── ensure-deploy-env.mts │ │ ├── ensure-env.mts │ │ ├── migrate-new.mts │ │ ├── smoke-test.mts │ │ └── worker-run.mts │ └── vite │ │ ├── __snapshots__ │ │ ├── transformServerFunctions.test.mts.snap │ │ └── useServerPlugin.test.mts.snap │ │ ├── checkIsUsingPrisma.mts │ │ ├── configPlugin.mts │ │ ├── createDirectiveLookupPlugin.mts │ │ ├── directivesPlugin.mts │ │ ├── ensureAliasArray.mts │ │ ├── index.mts │ │ ├── injectVitePreamblePlugin.mts │ │ ├── invalidateCacheIfPrismaClientChanged.mts │ │ ├── miniflarePlugin.mts │ │ ├── moveStaticAssetsPlugin.mts │ │ ├── normalizeModulePath.mts │ │ ├── prismaPlugin.mts │ │ ├── reactConditionsResolverPlugin.mts │ │ ├── redwoodPlugin.mts │ │ ├── ssrBridgePlugin.mts │ │ ├── transformClientComponents.mts │ │ ├── transformClientComponents.test.mts │ │ ├── transformJsxScriptTagsPlugin.mts │ │ ├── transformJsxScriptTagsPlugin.test.mts │ │ ├── transformServerFunctions.mts │ │ ├── transformServerFunctions.test.mts │ │ ├── useClientLookupPlugin.mts │ │ ├── useServerLookupPlugin.mts │ │ ├── useServerPlugin.mts │ │ ├── virtualPlugin.mts │ │ └── vitePreamblePlugin.mts ├── tsconfig.json └── types │ ├── global.d.ts │ ├── react.d.ts │ └── vite.d.ts ├── starters ├── minimal │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── Document.tsx │ │ │ ├── headers.ts │ │ │ ├── pages │ │ │ │ └── Home.tsx │ │ │ └── shared │ │ │ │ └── links.ts │ │ ├── client.tsx │ │ └── worker.tsx │ ├── tsconfig.json │ ├── types │ │ ├── rw.d.ts │ │ └── vite.d.ts │ ├── vite.config.mts │ ├── worker-configuration.d.ts │ └── wrangler.jsonc └── standard │ ├── .devcontainer │ ├── Dockerfile │ └── devcontainer.json │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── migrations │ └── 0001_init.sql │ ├── package.json │ ├── prisma │ └── schema.prisma │ ├── src │ ├── app │ │ ├── Document.tsx │ │ ├── headers.ts │ │ ├── pages │ │ │ ├── Home.tsx │ │ │ └── user │ │ │ │ ├── Login.tsx │ │ │ │ ├── functions.ts │ │ │ │ └── routes.ts │ │ └── shared │ │ │ └── links.ts │ ├── client.tsx │ ├── db.ts │ ├── scripts │ │ └── seed.ts │ ├── session │ │ ├── durableObject.ts │ │ └── store.ts │ └── worker.tsx │ ├── tsconfig.json │ ├── types │ ├── rw.d.ts │ └── vite.d.ts │ ├── vite.config.mts │ ├── worker-configuration.d.ts │ └── wrangler.jsonc └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.github/workflows/check-sdk.yml: -------------------------------------------------------------------------------- 1 | name: SDK Checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "sdk/**" 9 | pull_request: 10 | paths: 11 | - "sdk/**" 12 | 13 | jobs: 14 | check-sdk: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 22 25 | 26 | - run: | 27 | corepack enable 28 | pnpm install 29 | 30 | - name: Build SDK 31 | run: pnpm build 32 | working-directory: sdk 33 | 34 | - name: Run tests 35 | run: pnpm test 36 | working-directory: sdk 37 | -------------------------------------------------------------------------------- /.github/workflows/check-starters.yml: -------------------------------------------------------------------------------- 1 | name: Starters Checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - "docs/**" 9 | pull_request: 10 | paths-ignore: 11 | - "docs/**" 12 | 13 | jobs: 14 | check-starters: 15 | runs-on: ubuntu-latest 16 | 17 | # context(justinvdm, 2025-05-12): 18 | # Only run this job for PRs from the same repository and pushes to main 19 | # For security, GH won't expose secrets for fork PRs 20 | if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Setup Node.js 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: 22 30 | 31 | - run: | 32 | corepack enable 33 | pnpm install 34 | 35 | - name: Link latest SDK 36 | run: pnpm link-sdk 37 | 38 | - name: Run checks for each starter 39 | run: pnpm -r --filter="@redwoodjs/starter-*" check 40 | 41 | - name: Test dev server for each starter 42 | run: pnpm check-starters:dev 43 | 44 | - name: Test building and preview server for each starter 45 | run: pnpm check-starters:preview 46 | -------------------------------------------------------------------------------- /.github/workflows/release-starters.yml: -------------------------------------------------------------------------------- 1 | name: Release Starters 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" # Trigger on any tag push 7 | - "!v*.*.*-*" # Exclude pre-release tags (e.g., v1.2.3-alpha) 8 | workflow_dispatch: 9 | inputs: 10 | version: 11 | description: "Version name (optional, used for filenames)" 12 | required: false 13 | default: "" 14 | 15 | jobs: 16 | release: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Get tag or input version 24 | id: vars 25 | run: | 26 | if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.version }}" ]; then 27 | echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT 28 | else 29 | echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT 30 | fi 31 | 32 | - name: Package each starter folder 33 | run: | 34 | mkdir -p output 35 | for dir in ./starters/*/ ; do 36 | folder_name=$(basename "$dir") 37 | tar -czvf output/${folder_name}-${{ steps.vars.outputs.version }}.tar.gz -C "$dir" . 38 | zip -r output/${folder_name}-${{ steps.vars.outputs.version }}.zip "$dir" 39 | done 40 | 41 | - name: Upload all packaged starters to GitHub Release 42 | uses: softprops/action-gh-release@v2 43 | with: 44 | files: output/* 45 | tag_name: ${{ steps.vars.outputs.version }} 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": "*.jsonc", 5 | "options": { 6 | "trailingComma": "none" 7 | } 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "cicd", 4 | "redwoodjs" 5 | ], 6 | "workbench.colorCustomizations": { 7 | "activityBar.background": "#152966", 8 | "titleBar.activeBackground": "#1D398E", 9 | "titleBar.activeForeground": "#FBFCFE" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 RedwoodJS Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /SMOKE-TESTING.md: -------------------------------------------------------------------------------- 1 | # RedwoodSDK Starter Smoke Testing 2 | 3 | This document explains the smoke testing process for RedwoodJS starter templates. 4 | 5 | ## Overview 6 | 7 | The smoke tests checks that the critical paths of the sdk work for a project. 8 | 9 | - It runs the whole flow: from install, to dev, to release. 10 | - For both dev and production, it checks that {server,client} components render, that client components get action results, and that server components re-render as a result of client-initiated actions. 11 | - It then upgrades to realtime and does these same checks again (for both dev in production). 12 | - For dev, we also check that HMR works for server and client components. 13 | 14 | ## Running Smoke Tests Locally 15 | 16 | To run smoke tests for a project, run this from within the `sdk/` dir: 17 | 18 | ```sh 19 | pnpm smoke-test /user/login --path= --sync --keep 20 | 21 | # example 22 | pnpm smoke-test /user/login --path=../starters/standard --sync --keep 23 | ``` 24 | 25 | Or if you do not have a checkout of the sdk 26 | 27 | ```sh 28 | # From within your project directory 29 | npx rw-scripts smoke-tests 30 | ``` 31 | 32 | ## How It Works 33 | 34 | The smoke testing process: 35 | 36 | 1. Uses the SDK's smoke-test script for each starter template 37 | 2. Copies each starter to a temporary directory with a unique name 38 | 3. Starts a development server 39 | 4. Performs health checks using Puppeteer and Chrome 40 | 5. Attempts to upgrade to realtime mode and perform checks again 41 | 6. Runs `npm run release` 42 | 7. Runs the same checks, now for production 43 | 8. Saves screenshots as artifacts for debugging 44 | 45 | ## CI Integration 46 | 47 | The smoke tests run automatically in GitHub Actions when: 48 | 49 | - Code is pushed to the main branch 50 | - A pull request is opened (for non-forks, for security reasons) 51 | - The workflow is manually triggered 52 | 53 | Screenshots are stored as GitHub artifacts for 7 days. 54 | 55 | ## Troubleshooting 56 | 57 | If a smoke test fails: 58 | 59 | 1. Check the logs for error messages (you can also find the logs as a file in the artifacts dir) 60 | 2. Examine the screenshot artifacts to see what the browser was displaying 61 | 3. Try running the tests locally 62 | 4. You'll also find a copy of the project used for smoke tests in the artifacts dir. You can try running dev/release using this project manually 63 | 64 | You can find the artifacts in `.artifacts` for the directory you ran the smoke test command in. 65 | -------------------------------------------------------------------------------- /docs/.eslintignore: -------------------------------------------------------------------------------- 1 | astro.config.mjs -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /docs/.node-version: -------------------------------------------------------------------------------- 1 | 22.14.0 2 | -------------------------------------------------------------------------------- /docs/.prettierignore: -------------------------------------------------------------------------------- 1 | astro.config.mjs -------------------------------------------------------------------------------- /docs/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /docs/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /docs/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["redwoodsdk", "rwsdk", "svgs", "Wize"], 3 | "workbench.colorCustomizations": { 4 | "activityBar.background": "#540B4D", 5 | "titleBar.activeBackground": "#760F6C", 6 | "titleBar.activeForeground": "#FEFBFE" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | RedwoodSDK logo 6 | 7 | 8 | 9 |

RedwoodSDK

10 | 11 | Redwood Inc. logo 12 | Documentation 13 | Join the community on Discord 14 | License 15 |
16 | 17 | # RedwoodSDK Docs 18 | 19 | ## Usage 20 | 21 | ```bash 22 | pnpm install 23 | pnpm dev 24 | ``` 25 | 26 | ## Style guide 27 | 28 | These are simple guidelines that help us keep the documentation consistent. 29 | 30 | 1. Always refer to "RedwoodSDK" as "RedwoodSDK," not "Redwood," "RedwoodJS" or "Redwood SDK." 31 | 2. Use "Request/Response" rather than "Request / Response" or "Request/ Response." 32 | 3. Use "Route handler" rather than "Route function." 33 | 34 | ## Code Syntax 35 | 36 | Prefer "bash showLineNumbers=false" when only showing code commands. 37 | 38 | Example: 39 | 40 | ```bash showLineNumbers=false 41 | npx create-rwsdk 42 | ``` 43 | 44 | Prefer "bash showLineNumbers=false withOutput" when showing both code commands along with the expected output. 45 | 46 | Example 47 | 48 | ```bash showLineNumbers=false withOutput 49 | > npx wrangler d1 create my-database 50 | 51 | ✅ Successfully created DB 'my-database' in region WEUR 52 | Created your new D1 database. 53 | 54 | [[d1_databases]] 55 | binding = "DB" 56 | database_name = "my-database" 57 | database_id = "62x40823-4750-4973-b994-fb8fd55xxxx6" 58 | ``` 59 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | - [ ] Add analytics for the site docs 2 | 3 | # Getting Started 4 | 5 | ## Installation 6 | 7 | - [ ] Both Next.js and Remix have additional documentation on their "installation" pages for a manual install, if you don't want to use their create apps 8 | 9 | # Tutorial 10 | 11 | - [ ] Add `alt` text for all the screenshots 12 | - [ ] Make sure that there aren't any broken images []() 13 | -------------------------------------------------------------------------------- /docs/ec.config.mjs: -------------------------------------------------------------------------------- 1 | import { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections' 2 | import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers' 3 | import { pluginCodeCaption } from "@fujocoded/expressive-code-caption"; 4 | import { pluginCodeOutput } from "@fujocoded/expressive-code-output"; 5 | import { pluginColorChips } from 'expressive-code-color-chips'; 6 | 7 | /** @type {import('@astrojs/starlight/expressive-code').StarlightExpressiveCodeOptions} */ 8 | export default{ 9 | plugins: [pluginCodeCaption(), pluginCollapsibleSections(), pluginLineNumbers(), pluginCodeOutput(), pluginColorChips()], 10 | defaultProps: { 11 | // disable line numbers by default 12 | showLineNumbers: false, 13 | // But enable line numbers for certain languages 14 | overridesByLang: { 15 | 'js,ts,css,tsx,jsx': { 16 | showLineNumbers: true, 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "private": true, 4 | "type": "module", 5 | "version": "0.0.1", 6 | "scripts": { 7 | "dev": "astro dev", 8 | "start": "astro dev", 9 | "build": "astro build", 10 | "preview": "astro preview", 11 | "astro": "astro" 12 | }, 13 | "dependencies": { 14 | "@astrojs/mdx": "^4.2.1", 15 | "@astrojs/partytown": "^2.1.4", 16 | "@astrojs/starlight": "^0.32.0", 17 | "@expressive-code/plugin-collapsible-sections": "^0.40.2", 18 | "@expressive-code/plugin-line-numbers": "^0.40.2", 19 | "@fujocoded/expressive-code-caption": "^0.0.3", 20 | "@fujocoded/expressive-code-output": "^0.0.1", 21 | "astro": "^5.1.5", 22 | "astro-embed": "^0.9.0", 23 | "expressive-code-color-chips": "^0.1.2", 24 | "lite-youtube-embed": "^0.3.3", 25 | "sharp": "^0.32.5", 26 | "starlight-llms-txt": "^0.5.1", 27 | "starlight-package-managers": "^0.11.0" 28 | } 29 | } -------------------------------------------------------------------------------- /docs/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/assets/favicon.ico -------------------------------------------------------------------------------- /docs/src/assets/og-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/assets/og-docs.png -------------------------------------------------------------------------------- /docs/src/content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection, z } from "astro:content"; 2 | import { docsLoader } from "@astrojs/starlight/loaders"; 3 | import { docsSchema } from "@astrojs/starlight/schema"; 4 | 5 | export const collections = { 6 | docs: defineCollection({ 7 | loader: docsLoader(), 8 | schema: docsSchema({ 9 | extend: z.object({ 10 | banner: z 11 | .object({ 12 | content: z.string(), 13 | }) 14 | .default({ 15 | content: 16 | "4th April 2025: This is a preview, whilst production-ready, it means some APIs might change", 17 | }), 18 | }), 19 | }), 20 | }), 21 | }; 22 | -------------------------------------------------------------------------------- /docs/src/content/docs/core/cron.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cron Triggers 3 | description: Schedule background tasks 4 | --- 5 | 6 | If you want to schedule a background task, [Cloudflare supports Cron Triggers](https://developers.cloudflare.com/workers/configuration/cron-triggers/). 7 | 8 | ## Setup 9 | 10 | Within your `wrangler.jsonc` file, add a new section called `triggers`: 11 | 12 | ```jsonc title="wrangler.jsonc" 13 | "triggers": { 14 | "crons": ["* * * * *"] 15 | } 16 | --- 17 | Where `crons` includes an array of cron schedules. 18 | --- 19 | ``` 20 | 21 | Within your `worker.tsx` file, adjust your `defineApp` function: 22 | 23 | ```tsx 24 | const app = defineApp([ 25 | ... 26 | ]); 27 | 28 | export default { 29 | fetch: app.fetch, 30 | async scheduled(controller: ScheduledController) { 31 | switch (controller.cron) { 32 | // Runs every minute 33 | case "* * * * *": 34 | console.log("🧹 Clean up Unverified Users"); 35 | break; 36 | } 37 | console.log("⏰ cron processed"); 38 | }, 39 | } satisfies ExportedHandler; 40 | ``` 41 | 42 | Notice the `case` matches the cron schedule defined within the `wrangler.jsonc` file. 43 | 44 | In the example above, `console.log("🧹 Clean up Unverified Users");` runs every minute. However, you could account for different schedules by adding more triggers to your `wrangler.jsonc` file and more cases to your `switch` statement: 45 | 46 | ```jsonc title="wrangler.jsonc" 47 | "crons": ["* * * * *", "0 0 * * *"] 48 | ``` 49 | 50 | ```tsx title="worker.tsx" 51 | // Runs every hour 52 | case "0 0 * * *": 53 | console.log("🔄 Update something"); 54 | break; 55 | ``` 56 | 57 | ## Cloudflare Cron Triggers 58 | 59 | Within the Cloudflare Dashboard UI, you can view all the cron triggers for your worker. Click on the **Settings** tab, and then click on the **Cron Triggers** section. 60 | 61 | ![](./images/trigger-events.png) 62 | 63 | To view more details about the events, click on the **View Events** link. 64 | 65 | ![](./images/trigger-events-view-events.png) 66 | 67 | You can see a list of all the events that have been triggered for your worker. 68 | 69 | ![](./images/cloudflare-cron-events.png) 70 | 71 | ## Further Reading 72 | 73 | - [Cloudflare Cron Triggers](https://developers.cloudflare.com/workers/configuration/cron-triggers/) 74 | 75 | -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/active-domain-email-confirmation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/active-domain-email-confirmation.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/add-a-domain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/add-a-domain.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/check-nameservers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/check-nameservers.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/checking-nameservers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/checking-nameservers.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/cloudflare-cron-events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/cloudflare-cron-events.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/cloudflare-ui-for-env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/cloudflare-ui-for-env.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/enter-existing-domain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/enter-existing-domain.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/env-in-cursor-sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/env-in-cursor-sidebar.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/last-step-update-ns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/last-step-update-ns.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/review-dns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/review-dns.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/search-for-domain-cloudflare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/search-for-domain-cloudflare.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/select-plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/select-plan.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/trigger-events-view-events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/trigger-events-view-events.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/images/trigger-events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/core/images/trigger-events.png -------------------------------------------------------------------------------- /docs/src/content/docs/core/overview.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | description: Things you need to know about RedwoodSDK 4 | tableOfContents: false 5 | --- 6 | 7 | import { Aside } from "@astrojs/starlight/components"; 8 | import { YouTube } from "astro-embed"; 9 | 10 | Below are the things you need to know in order to be effective with RedwoodSDK. 11 | 12 | 13 | _Peter Pistorius gives a 5 minute tour of RedwoodSDK._ 14 | 15 | --- 16 | 17 | 1. [Request Handling, Routing & Responses](/core/routing) 18 | 1. [React Server Components](/core/react-server-components) 19 | 1. [Database](/core/database) 20 | 1. [Storage](/core/storage) 21 | 1. [Realtime](/core/realtime) 22 | 1. [Queues & Background Jobs](/core/queues) 23 | 1. [Authentication & Session Management](/core/authentication) 24 | 1. [Security](/core/security) 25 | 1. [Hosting (Cloudflare)](/core/hosting) 26 | -------------------------------------------------------------------------------- /docs/src/content/docs/getting-started/images/hello-world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/getting-started/images/hello-world.png -------------------------------------------------------------------------------- /docs/src/content/docs/getting-started/images/workers-pages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/getting-started/images/workers-pages.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/1-resend-homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/1-resend-homepage.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/2-resend-create-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/2-resend-create-account.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/3-resend-send-first-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/3-resend-send-first-email.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/4-resend-create-api-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/4-resend-create-api-key.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/installed-react-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/installed-react-email.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/react-email-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/react-email-dev.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/react-email-get-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/react-email-get-code.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/react-email-preview-in-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/react-email-preview-in-browser.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/email/images/react-email-template-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/email/images/react-email-template-files.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/og-html-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/og-html-example.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/og-react-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/og-react-example.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/opengraph-xyz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/opengraph-xyz.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-client-component-console-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-client-component-console-error.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-ctx-argtypes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-ctx-argtypes.gif -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-home-story-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-home-story-1.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-home-story-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-home-story-2.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-mocked-user-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-mocked-user-list.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-prisma-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-prisma-error.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-select-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-select-react.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-select-use.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-select-use.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-served-site.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-served-site.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/frontend/images/sb-started.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redwoodjs/sdk/2c2ca4e1d38922ffdb2d13e1d22ce27dfafedd17/docs/src/content/docs/guides/frontend/images/sb-started.png -------------------------------------------------------------------------------- /docs/src/content/docs/guides/rsc-streams.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Server Function Streams 3 | description: Sending chunked stream responses from server function to the client 4 | next: false 5 | --- 6 | 7 | This pattern is useful for sending partial responses to the client, such as when you're waiting for data from an external API, like an AI model. 8 | 9 | ## Example 10 | 11 | First create a server function that returns a stream. In this example we're using Cloudflare's AI, and we're streaming the response back to the client. 12 | 13 | ```tsx title="app/pages/Chat/functions.ts" lineNumbers mark={7} 14 | "use server"; 15 | 16 | export async function sendMessage(prompt: string) { 17 | console.log("Running AI with Prompt:", prompt); 18 | const response = await env.AI.run("@cf/meta/llama-4-scout-17b-16e-instruct", { 19 | prompt, 20 | stream: true, 21 | }); 22 | return response as unknown as ReadableStream; 23 | } 24 | ``` 25 | 26 | Now on the client component, we can use the `consumeEventStream` function to parse the chunks whilst keeping the UI updated. 27 | 28 | ```tsx title="app/pages/Chat/Chat.tsx" lineNumbers mark={17-29} 29 | "use client"; 30 | 31 | import { sendMessage } from "./functions"; 32 | import { useState } from "react"; 33 | import { consumeEventStream } from "rwsdk/client"; 34 | 35 | export function Chat() { 36 | const [message, setMessage] = useState(""); 37 | const [reply, setReply] = useState(""); 38 | const [isLoading, setIsLoading] = useState(false); 39 | const onSubmit = async (e: React.FormEvent) => { 40 | e.preventDefault(); 41 | 42 | setIsLoading(true); 43 | 44 | setReply(""); 45 | (await sendMessage(message)).pipeTo( 46 | consumeEventStream({ 47 | onChunk: (event) => { 48 | setReply((prev) => { 49 | if (event.data === "[DONE]") { 50 | setIsLoading(false); 51 | return prev; 52 | } 53 | return (prev += JSON.parse(event.data).response); 54 | }); 55 | }, 56 | }) 57 | ); 58 | }; 59 | 60 | return ( 61 |
62 |
{reply}
63 | 64 |
65 | setMessage(e.target.value)} 70 | /> 71 | 74 |
75 |
76 | ); 77 | } 78 | ``` 79 | 80 | For a working example, please see the [Chat example](https://github.com/redwoodjs/example-streaming-ai-chat/tree/main). 81 | -------------------------------------------------------------------------------- /docs/src/content/docs/reference/create-rwsdk.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: create-rwsdk 3 | description: A step-by-step guide for creating a new RedwoodSDK project. 4 | --- 5 | 6 | `create-rwsdk` is a command line tool for creating new RedwoodSDK projects 7 | 8 | ```bash 9 | npx create-rwsdk my-project 10 | ``` 11 | 12 | At Redwood, we try to reduce the magic as much as possible. So, even though we have a command line tool for creating new projects, it's important to us that we share what's happening under the hood. 13 | 14 | At it's core, the `create-rwsdk` command looks at the most recent GitHub release, downloads the attached `tar.gz` file, and extracts it to the current directory. 15 | 16 | ## Usage 17 | 18 | ```bash 19 | npx create-rwsdk [project-name] [options] 20 | ``` 21 | 22 | #### Commands 23 | 24 | - `create`: Create a new project (default command when no command is specified) 25 | - `list`: List and interactively select from available templates 26 | 27 | #### Arguments 28 | 29 | - `[project-name]`: Name of the project directory to create (optional, will prompt if not provided) 30 | 31 | #### Options 32 | 33 | - `-f, --force`: Force overwrite if directory exists 34 | - `-t, --template