diff --git a/remix-auth-pocketbase/.eslintrc.cjs b/remix-auth-pocketbase/.eslintrc.cjs
new file mode 100644
index 000000000..287534bb3
--- /dev/null
+++ b/remix-auth-pocketbase/.eslintrc.cjs
@@ -0,0 +1,30 @@
+/* eslint-env es6 */
+const OFF = 0;
+const WARN = 1;
+const ERROR = 2;
+
+/** @type {import('eslint').Linter.Config} */
+module.exports = {
+ root: true,
+ extends: ["@remix-run/eslint-config/internal", "plugin:markdown/recommended"],
+ plugins: ["markdown"],
+ settings: {
+ "import/internal-regex": "^~/",
+ },
+ ignorePatterns: ["pocketbase/**"],
+ rules: {
+ "prefer-let/prefer-let": OFF,
+ "prefer-const": WARN,
+
+ "import/order": [
+ ERROR,
+ {
+ alphabetize: { caseInsensitive: true, order: "asc" },
+ groups: ["builtin", "external", "internal", "parent", "sibling"],
+ "newlines-between": "always",
+ },
+ ],
+
+ "react/jsx-no-leaked-render": [WARN, { validStrategies: ["ternary"] }],
+ },
+};
diff --git a/remix-auth-pocketbase/.gitignore b/remix-auth-pocketbase/.gitignore
new file mode 100644
index 000000000..3f7bf98da
--- /dev/null
+++ b/remix-auth-pocketbase/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+
+/.cache
+/build
+/public/build
+.env
diff --git a/remix-auth-pocketbase/README.md b/remix-auth-pocketbase/README.md
new file mode 100644
index 000000000..5f860701a
--- /dev/null
+++ b/remix-auth-pocketbase/README.md
@@ -0,0 +1,49 @@
+# Pocketbase example
+
+This is an example showing a basic integration of Remix with [Pocketbase](https://pocketbase.io/).
+
+## Example
+
+### Getting started
+
+First, install dependencies in both the root folder (right here)
+
+```bash
+npm i
+```
+
+Then, start both the Remix and Pocketbase with
+
+```bash
+npm run dev
+```
+
+### Pocketbase
+
+In this example, a Pocketbase instance will be downloaded to `pocketbase/`. Using the migration framework, an admin user and app user will be created. A `realtime_example` collection will be created and supported with `pocketbase/pb_hooks/realtime.pb.js` by a `cronAdd` function. __In order for the email verification and forgot-password emails to work, you will need to setup SMTP in the Pocketbase admin.__ You can also manually verify new accounts in the Pocketbase admin for testing.
+
+> Note that in a real app, you'd likely not have your admin password commited in a migration. This is for demo purposes only.
+
+#### Administration Panel
+
+Pocketbase's administration panel is at [http://localhost:8090/_](http://localhost:8090/_).
+
+
+
+### Remix
+
+The Remix app is at http://localhost:3000. The following routes are provided:
+
+- __/__ - with links to the below
+- __/login__ - populated with the test user by default
+- __/register__ - populated with `2+pocketbase@remix.example` by default
+- __/forgot-password__ - populated with the test user's email by default
+- __/admin__ - accessible only after login and count is auto updated by way of Pocketbase's Realtime API
+
+There are two Pocketbase files, `pb.server.ts` and `pb.client.ts`. `pb.server.ts` handles the connection to the server for the auth and setting the cookies for persistence. It can also be used in the `loader` functions to prepopulate data on the server. `pb.client.ts` creates a new Pocketbase instance for the client. It uses the cookie setup on server for authenticating. You can use the client export for `useEffect` hooks or the realtime data API. `admin.tsx` has an example of loading data on the server and the realtime API.
+
+You may want to implement a `Content Security Policy` as this setup requires `httpOnly: false` set on the Pocketbase cookie to share between the server and client. This demo does not cover CSP.
diff --git a/remix-auth-pocketbase/app/pb.client.ts b/remix-auth-pocketbase/app/pb.client.ts
new file mode 100644
index 000000000..35d3f8e3a
--- /dev/null
+++ b/remix-auth-pocketbase/app/pb.client.ts
@@ -0,0 +1,8 @@
+import Pocketbase from "pocketbase";
+
+export let pb: Pocketbase | null = null;
+
+if (typeof window !== "undefined") {
+ pb = new Pocketbase(window.ENV.POCKETBASE_URL);
+ pb.authStore.loadFromCookie(document.cookie);
+}
diff --git a/remix-auth-pocketbase/app/pb.server.ts b/remix-auth-pocketbase/app/pb.server.ts
new file mode 100644
index 000000000..ff9927623
--- /dev/null
+++ b/remix-auth-pocketbase/app/pb.server.ts
@@ -0,0 +1,41 @@
+import { redirect } from "@remix-run/node";
+import Pocketbase from "pocketbase";
+
+export function getPocketbase(request?: Request) {
+ const pb = new Pocketbase(
+ process.env.POCKETBASE_URL || "http://localhost:8090",
+ );
+
+ if (request) {
+ pb.authStore.loadFromCookie(request.headers.get("cookie") || "");
+ } else {
+ pb.authStore.loadFromCookie("");
+ }
+
+ return pb;
+}
+
+export function getUser(pb: Pocketbase) {
+ if (pb.authStore.model) {
+ return structuredClone(pb.authStore.model);
+ }
+
+ return null;
+}
+
+export function createSession(redirectTo: string, pb: Pocketbase) {
+ return redirect(redirectTo, {
+ headers: {
+ "set-cookie": pb.authStore.exportToCookie({
+ secure: redirectTo.startsWith("https:"),
+ httpOnly: false,
+ }),
+ },
+ });
+}
+
+export function destroySession(pb: Pocketbase) {
+ pb.authStore.clear();
+
+ return createSession("/", pb);
+}
diff --git a/remix-auth-pocketbase/app/root.tsx b/remix-auth-pocketbase/app/root.tsx
new file mode 100644
index 000000000..7e1c3ce7e
--- /dev/null
+++ b/remix-auth-pocketbase/app/root.tsx
@@ -0,0 +1,50 @@
+import { cssBundleHref } from "@remix-run/css-bundle";
+import type { LinksFunction } from "@remix-run/node";
+import { json } from "@remix-run/node";
+import {
+ Links,
+ LiveReload,
+ Meta,
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+ useLoaderData,
+} from "@remix-run/react";
+
+export const links: LinksFunction = () => [
+ ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
+];
+
+export async function loader() {
+ return json({
+ ENV: {
+ POCKETBASE_URL: process.env.POCKETBASE_URL || "http://localhost:8090",
+ },
+ });
+}
+
+export default function App() {
+ const data = useLoaderData();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/remix-auth-pocketbase/app/routes/_index.tsx b/remix-auth-pocketbase/app/routes/_index.tsx
new file mode 100644
index 000000000..676b2e980
--- /dev/null
+++ b/remix-auth-pocketbase/app/routes/_index.tsx
@@ -0,0 +1,37 @@
+import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
+import { json } from "@remix-run/node";
+import { Link, useLoaderData } from "@remix-run/react";
+
+import { getPocketbase, getUser } from "~/pb.server";
+
+export const meta: MetaFunction = () => {
+ return [
+ { title: "New Remix App" },
+ { name: "description", content: "Welcome to Remix!" },
+ ];
+};
+
+export async function loader({ request }: LoaderFunctionArgs) {
+ const pb = getPocketbase(request);
+ const user = getUser(pb);
+
+ return json({ user });
+}
+
+export default function Index() {
+ const data = useLoaderData();
+
+ return (
+