diff --git a/examples/auth-router-provider/.gitignore b/examples/auth-router-provider/.gitignore
new file mode 100644
index 0000000000..d451ff16c1
--- /dev/null
+++ b/examples/auth-router-provider/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
diff --git a/examples/auth-router-provider/.stackblitzrc b/examples/auth-router-provider/.stackblitzrc
new file mode 100644
index 0000000000..d98146f4d0
--- /dev/null
+++ b/examples/auth-router-provider/.stackblitzrc
@@ -0,0 +1,4 @@
+{
+ "installDependencies": true,
+ "startCommand": "npm run dev"
+}
diff --git a/examples/auth-router-provider/README.md b/examples/auth-router-provider/README.md
new file mode 100644
index 0000000000..f18f905f1e
--- /dev/null
+++ b/examples/auth-router-provider/README.md
@@ -0,0 +1,28 @@
+---
+title: Authentication (using RouterProvider)
+toc: false
+---
+
+# Auth Example (using RouterProvider)
+
+This example demonstrates how to restrict access to routes to authenticated users when using ``.
+
+The primary difference compared to how authentication was handled in `BrowserRouter` is that since `RouterProvider` decouples fetching from rendering, we can no longer rely on React context and/or hooks to get our user authentication status. We need access to this information outside of the React tree so we can use it in our route `loader` and `action` functions.
+
+For some background information on this design choice, please check out the [Remixing React Router](https://remix.run/blog/remixing-react-router) blog post and Ryan's [When to Fetch](https://www.youtube.com/watch?v=95B8mnhzoCM) talk from Reactathon.
+
+Be sure to pay attention to the following features in this example:
+
+- The use of a standalone object _outside of the React tree_ that manages our authentication state
+- The use of `loader` functions to check for user authentication
+- The use of `redirect` from the `/protexted` `loader` when the user is not logged in
+- The use of a `
} />
+ );
+}
+
+function Layout() {
+ return (
+
+
Auth Example using RouterProvider
+
+
+ This example demonstrates a simple login flow with three pages: a public
+ page, a protected page, and a login page. In order to see the protected
+ page, you must first login. Pretty standard stuff.
+
+
+
+ First, visit the public page. Then, visit the protected page. You're not
+ yet logged in, so you are redirected to the login page. After you login,
+ you are redirected back to the protected page.
+
+
+
+ Notice the URL change each time. If you click the back button at this
+ point, would you expect to go back to the login page? No! You're already
+ logged in. Try it out, and you'll see you go back to the page you
+ visited just *before* logging in, the public page.
+
+
+
+
+
+
+ Public Page
+
+
+ Protected Page
+
+
+
+
+
+ );
+}
+
+function AuthStatus() {
+ // Get our logged in user, if they exist, from the root route loader data
+ let { user } = useRouteLoaderData("root") as { user: string | null };
+ let fetcher = useFetcher();
+
+ if (!user) {
+ return
+ );
+}
+
+async function loginAction({ request }: LoaderFunctionArgs) {
+ let formData = await request.formData();
+ let username = formData.get("username") as string | null;
+
+ // Validate our form inputs and return validation errors via useActionData()
+ if (!username) {
+ return {
+ error: "You must provide a username to log in",
+ };
+ }
+
+ // Sign in and redirect to the proper destination if successful.
+ try {
+ await fakeAuthProvider.signin(username);
+ } catch (error) {
+ // Unused as of now but this is how you would handle invalid
+ // username/password combinations - just like validating the inputs
+ // above
+ return {
+ error: "Invalid login attempt",
+ };
+ }
+
+ let redirectTo = formData.get("redirectTo") as string | null;
+ return redirect(redirectTo || "/");
+}
+
+async function loginLoader() {
+ if (fakeAuthProvider.isAuthenticated) {
+ return redirect("/");
+ }
+ return null;
+}
+
+function LoginPage() {
+ let location = useLocation();
+ let params = new URLSearchParams(location.search);
+ let from = params.get("from") || "/";
+
+ let navigation = useNavigation();
+ let isLoggingIn = navigation.formData?.get("username") != null;
+
+ let actionData = useActionData() as { error: string } | undefined;
+
+ return (
+
+
You must log in to view the page at {from}
+
+
+
+ );
+}
+
+function PublicPage() {
+ return
Public
;
+}
+
+function protectedLoader({ request }: LoaderFunctionArgs) {
+ // If the user is not logged in and tries to access `/protected`, we redirect
+ // them to `/login` with a `from` parameter that allows login to redirect back
+ // to this page upon successful authentication
+ if (!fakeAuthProvider.isAuthenticated) {
+ let params = new URLSearchParams();
+ params.set("from", new URL(request.url).pathname);
+ return redirect("/login?" + params.toString());
+ }
+ return null;
+}
+
+function ProtectedPage() {
+ return