Remix Auth is a complete open-source authentication solution for Remix.run applications.

Last update: Jun 16, 2022

Remix Auth

Simple Authentication for Remix

Features

  • Full Server-Side Authentication
  • Complete TypeScript Support
  • Strategy-based Authentication
  • Easily handle success and failure
  • Implement custom strategies
  • Supports persistent sessions

Overview

Remix Auth is a complete open-source authentication solution for Remix.run applications.

Heavily inspired by Passport.js, but completely rewrote it from scratch to work on top of the Web Fetch API. Remix Auth can be dropped in to any Remix-based application with minimal setup.

As with Passport.js, it uses the strategy pattern to support the different authentication flows. Each strategy is published individually as a separate npm package.

Installation

To use it, install it from npm (or yarn):

npm install remix-auth

Also, install one of the strategies. A list of strategies is available in the Community Strategies discussion.

Usage

Remix Auth needs a session storage object to store the user session. It can be any object that implements the SessionStorage interface from Remix.

In this example I'm using the createCookieSessionStorage function.

// app/services/session.server.ts
import { createCookieSessionStorage } from "@remix-run/node";

// export the whole sessionStorage object
export let sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "_session", // use any name you want here
    sameSite: "lax", // this helps with CSRF
    path: "/", // remember to add this so the cookie will work in all routes
    httpOnly: true, // for security reasons, make this cookie http only
    secrets: ["s3cr3t"], // replace this with an actual secret
    secure: process.env.NODE_ENV === "production", // enable this in prod only
  },
});

// you can also export the methods individually for your own usage
export let { getSession, commitSession, destroySession } = sessionStorage;

Now, create a file for the Remix Auth configuration. Here import the Authenticator class and your sessionStorage object.

(sessionStorage);">
// app/services/auth.server.ts
import { Authenticator } from "remix-auth";
import { sessionStorage } from "~/services/session.server";

// Create an instance of the authenticator, pass a generic with what
// strategies will return and will store in the session
export let authenticator = new Authenticator<User>(sessionStorage);

The User type is whatever you will store in the session storage to identify the authenticated user. It can be the complete user data or a string with a token. It is completely configurable.

After that, register the strategies. In this example, we will use the FormStrategy to check the documentation of the strategy you want to use to see any configuration you may need.

{ let email = form.get("email"); let password = form.get("password"); let user = await login(email, password); // the type of this user must match the type you pass to the Authenticator // the strategy will automatically inherit the type if you instantiate // directly inside the `use` method return user; }), // each strategy has a name and can be changed to use another one // same strategy multiple times, especially useful for the OAuth2 strategy. "user-pass" );">
import { FormStrategy } from "remix-auth-form";

// Tell the Authenticator to use the form strategy
authenticator.use(
  new FormStrategy(async ({ form }) => {
    let email = form.get("email");
    let password = form.get("password");
    let user = await login(email, password);
    // the type of this user must match the type you pass to the Authenticator
    // the strategy will automatically inherit the type if you instantiate
    // directly inside the `use` method
    return user;
  }),
  // each strategy has a name and can be changed to use another one
  // same strategy multiple times, especially useful for the OAuth2 strategy.
  "user-pass"
);

Now that at least one strategy is registered, it is time to set up the routes.

First, create a /login page. Here we will render a form to get the email and password of the user and use Remix Auth to authenticate the user.

); } // Second, we need to export an action function, here we will use the // `authenticator.authenticate method` export let action: ActionFunction = async ({ request }) => { // we call the method with the name of the strategy we want to use and the // request object, optionally we pass an object with the URLs we want the user // to be redirected to after a success or a failure return await authenticator.authenticate("user-pass", request, { successRedirect: "/dashboard", failureRedirect: "/login", }); }; // Finally, we can export a loader function where we check if the user is // authenticated with `authenticator.isAuthenticated` and redirect to the // dashboard if it is or return null if it's not export let loader: LoaderFunction = async ({ request }) => { // If the user is already authenticated redirect to /dashboard directly return await authenticator.isAuthenticated(request, { successRedirect: "/dashboard", }); };">
// app/routes/login.tsx
import { Form } from "@remix-run/react"
import { ActionFunction, LoaderFunction, redirect } from "@remix-run/node"
import { authenticator } from "~/auth.server";

// First we create our UI with the form doing a POST and the inputs with the
// names we are going to use in the strategy
export default function Screen() {
  return (
    <Form method="post">
      <input type="email" name="email" required />
      <input
        type="password"
        name="password"
        autoComplete="current-password"
        required
      />
      <button>Sign In</button>
    </Form>
  );
}

// Second, we need to export an action function, here we will use the
// `authenticator.authenticate method`
export let action: ActionFunction = async ({ request }) => {
  // we call the method with the name of the strategy we want to use and the
  // request object, optionally we pass an object with the URLs we want the user
  // to be redirected to after a success or a failure
  return await authenticator.authenticate("user-pass", request, {
    successRedirect: "/dashboard",
    failureRedirect: "/login",
  });
};

// Finally, we can export a loader function where we check if the user is
// authenticated with `authenticator.isAuthenticated` and redirect to the
// dashboard if it is or return null if it's not
export let loader: LoaderFunction = async ({ request }) => {
  // If the user is already authenticated redirect to /dashboard directly
  return await authenticator.isAuthenticated(request, {
    successRedirect: "/dashboard",
  });
};

With this, we have our login page. If we need to get the user data in another route of the application, we can use the authenticator.isAuthenticated method passing the request this way:

// get the user data or redirect to /login if it failed
let user = await authenticator.isAuthenticated(request, {
  failureRedirect: "/login",
});

// if the user is authenticated, redirect to /dashboard
await authenticator.isAuthenticated(request, {
  successRedirect: "/dashboard",
});

// get the user or null, and do different things in your loader/action based on
// the result
let user = await authenticator.isAuthenticated(request);
if (user) {
  // here the user is authenticated
} else {
  // here the user is not authenticated
}

Once the user is ready to leave the application, we can call the logout method inside an action.

export let action: ActionFunction = async ({ request }) => {
  await authenticator.logout(request, { redirectTo: "/login" });
};

Advanced Usage

Custom redirect URL based on the user

Say we have /dashboard and /onboarding routes, and after the user authenticates, you need to check some value in their data to know if they are onboarded or not.

If we do not pass the successRedirect option to the authenticator.authenticate method, it will return the user data.

Note that we will need to store the user data in the session this way. To ensure we use the correct session key, the authenticator has a sessionKey property.

export let action: ActionFunction = async ({ request }) => {
  let user = await authenticator.authenticate("user-pass", request, {
    failureRedirect: "/login",
  });

  // manually get the session
  let session = await getSession(request.headers.get("cookie"));
  // and store the user data
  session.set(authenticator.sessionKey, user);

  // commit the session
  let headers = new Headers({ "Set-Cookie": await commitSession(session) });

  // and do your validation to know where to redirect the user
  if (isOnboarded(user)) return redirect("/dashboard", { headers });
  return redirect("/onboarding", { headers });
};

Changing the session key

If we want to change the session key used by Remix Auth to store the user data, we can customize it when creating the Authenticator instance.

export let authenticator = new Authenticator<AccessToken>(sessionStorage, {
  sessionKey: "accessToken",
});

With this, both authenticate and isAuthenticated will use that key to read or write the user data (in this case, the access token).

If we need to read or write from the session manually, remember always to use the authenticator.sessionKey property. If we change the key in the Authenticator instance, we will not need to change it in the code.

Reading authentication errors

When the user cannot authenticate, the error will be set in the session using the authenticator.sessionErrorKey property.

We can customize the name of the key when creating the Authenticator instance.

export let authenticator = new Authenticator<User>(sessionStorage, {
  sessionErrorKey: "my-error-key",
});

Furthermore, we can read the error using that key after a failed authentication.

// in the loader of the login route
export let loader: LoaderFunction = async ({ request }) => {
  await authenticator.isAuthenticated(request, {
    successRedirect: "/dashboard",
  });
  let session = await getSession(request.headers.get("cookie"));
  let error = session.get(authenticator.sessionErrorKey);
  return json({ error });
};

Remember always to use the authenticator.sessionErrorKey property. If we change the key in the Authenticator instance, we will not need to change it in the code.

Errors Handling

By default, any error in the authentication process will throw a Response object. If failureRedirect is specified, this will always be a redirect response with the error message on the sessionErrorKey.

If a failureRedirect is not defined, Remix Auth will throw a 401 Unauthorized response with a JSON body containing the error message. This way, we can use the CatchBoundary component of the route to render any error message.

If we want to get an error object inside the action instead of throwing a Response, we can configure the throwOnError option to true. We can do this when instantiating the Authenticator or calling authenticate.

If we do it in the Authenticator, it will be the default behavior for all the authenticate calls.

export let authenticator = new Authenticator<User>(sessionStorage, {
  throwOnError: true,
});

Alternatively, we can do it on the action itself.

{ try { return await authenticator.authenticate("user-pass", request, { successRedirect: "/dashboard", throwOnError: true, }); } catch (error) { // Because redirects work by throwing a Response, you need to check if the // caught error is a response and return it or throw it again if (error instanceof Response) return error; if (error instanceof AuthorizationError) { // here the error is related to the authentication process } // here the error is a generic error that another reason may throw } };">
import { AuthorizationError } from "remix-auth";

export let action: ActionFunction = async ({ request }) => {
  try {
    return await authenticator.authenticate("user-pass", request, {
      successRedirect: "/dashboard",
      throwOnError: true,
    });
  } catch (error) {
    // Because redirects work by throwing a Response, you need to check if the
    // caught error is a response and return it or throw it again
    if (error instanceof Response) return error;
    if (error instanceof AuthorizationError) {
      // here the error is related to the authentication process
    }
    // here the error is a generic error that another reason may throw
  }
};

If we define both failureRedirect and throwOnError, the redirect will happen instead of throwing an error.

GitHub

https://github.com/sergiodxa/remix-auth
Comments
  • 1. How to display failure

    Hey, I want to display the AuthenticationError that is thrown in the login function on the sign in page. Any suggestions on how I could do that or is this even possible?

    Thanks

    Reviewed by JSLNO at 2021-12-08 11:47
  • 2. "State doesn't match" authorization error after POST status 302(redirect) in the OAuth2 flow

    Hi, Thank you for this package, it is very useful. I found an issue while using it with AWS Cognito.

    After a POST with status 302(redirect) in the OAuth2 flow, the redirect location url has the "state" parameter already decoded and will not match anymore the session state.

    I think the correct fix is to encode/decode state when set-to/get-from URLSearchParams but not encode it in the session. This will work with in any situation, decoding an already decoded value is the same.

    async authorize(sessionStorage, session) {
      let state = randomBytes(100).toString("base64");
      session.set(this.sessionStateKey, state);
      ...
      params.set("state", encodeURIComponent(state));
      ...
    }
    
    async authenticate(request, sessionStorage, options, callback) {
      ...
       let state = url.searchParams.get("state");
      if (!state)
            throw new AuthorizationError("Missing state.");
      state = decodeURIComponent(state);
      
      if (session.get(this.sessionStateKey) === state) {
      ...
    }
    
    Reviewed by cipinistor at 2021-10-12 11:23
  • 3. Broken on Netlify Functions

    Describe the bug

    I upgraded to the latest versions and now I get this error message on post during authorize:

    Error: cannot clone body after it is used
        at Authenticator.authenticate (/var/task/node_modules/remix-auth/build/authenticator.js:86:49)
    

    Full call stack

    Jun 15, 10:26:23 AM: 24f9d30b ERROR  Error: cannot clone body after it is used
        at clone (/var/task/node_modules/@remix-run/web-fetch/src/body.js:283:9)
        at new Request (/var/task/node_modules/@remix-run/web-fetch/src/request.js:88:6)
        at NodeRequest.clone (/var/task/node_modules/@remix-run/web-fetch/src/request.js:226:10)
        at NodeRequest.clone (/var/task/node_modules/@remix-run/node/fetch.js:28:18)
        at Authenticator.authenticate (/var/task/node_modules/remix-auth/build/authenticator.js:86:49)
        at action11 (/var/task/.netlify/functions-internal/server.js:6253:32)
        at Object.callRouteAction (/var/task/node_modules/@remix-run/server-runtime/data.js:40:14)
        at handleDataRequest (/var/task/node_modules/@remix-run/server-runtime/server.js:94:18)
        at requestHandler (/var/task/node_modules/@remix-run/server-runtime/server.js:34:18)
        at Runtime.handler (/var/task/node_modules/@remix-run/netlify/server.js:35:20)
        
    

    Works like a charm locally

    package.json extract :

        "dependencies": {
          "@netlify/functions": "^1.0.0",
          "@ramp-network/ramp-instant-sdk": "3.1.1",
          "@reduxjs/toolkit": "1.8.2",
          "@remix-run/netlify": "^1.6.0",
          "@remix-run/react": "^1.6.0",
          "remix-auth": "3.2.2",
          "remix-utils": "3.3.0",
          "remix": "1.6.0",
      },
    

    Your Example Website or App

    ping me in private

    Steps to Reproduce the Bug or Issue

    I wish it was that simple.

    Expected behavior

    Not error out.

    Screenshots or Videos

    No response

    Platform

    Netlify

    Additional context

    No response

    Reviewed by mwawrusch at 2022-06-15 17:37
  • 4. Replace `request.clone()` with `new Request(...)`

    I received this warning while running in Cloudflare Workers:

    Your worker called response.clone(), but did not read the body of both clones. 
    This is wasteful, as it forces the system to buffer the entire response body in memory, rather than streaming it through. 
    This may cause your worker to be unexpectedly terminated for going over the memory limit. 
    If you only meant to copy the response headers and metadata (e.g. in order to be able to modify them), use `new Response(response.body, response)` instead
    

    This is the only .clone() I could find in my worker, and updating this removed the warning. My app still appears to be working correctly

    Reviewed by jfsiii at 2022-05-05 11:12
  • 5. Example callback will leak user session to client

    Describe the bug

    The use of returning the result of authenticator.authenticate in the examples allows the client to re-submit the request once logged in and get a json response of the user session. Usually this content is protected by storage or encrypted cookies.

    https://github.com/sergiodxa/remix-auth/blob/main/README.md?plain=1#L124-L132

    This applies to the examples in the remix-auth-oauth2 repo even more since it's probable for access tokens to be stored in the user session.

    A better approach would be simply await it and then throw an error:

      await authenticator.authenticate("user-pass", request, {
        successRedirect: "/dashboard",
        failureRedirect: "/login",
      });
    
      throw new Error("already logged in");
    

    Your Example Website or App

    N/A

    Steps to Reproduce the Bug or Issue

    N/A

    Expected behavior

    I expect the examples to show a more secure path.

    Screenshots or Videos

    No response

    Platform

    N/A

    Additional context

    No response

    Reviewed by ngbrown at 2022-03-13 04:51
  • 6. Abstract authentication checker for token refreshing and user data verification

    There seems to be a missing feature, like when every page calls isAuthentication, but it doesn't verify the user is authorized. E.g., if a user gets blocked, they will still have access to the page, but they should destroy their session.

    This will provide support for adding

    public async isAuthenticated(
        request: Request,
        user: User | null,
        options:
          | { successRedirect?: never; failureRedirect?: never }
          | { successRedirect: string; failureRedirect?: never }
          | { successRedirect?: never; failureRedirect: string } = {}
      ): Promise<User | null> {
      // Validate the current user data or refresh the token here. 
      // You will need to return a new commit if you wish to refresh the token. 
      // This will also allow for returning Response like, the default isAuthentication
    
      return super.isAuthenticated(request, user, options)
    }
    

    Another suggestion: Should we extend the options for isAuthenticated to allow custom properties like role to pass through this?

    Note: This pull request also changes API updates to the tests. The changes update the API from Remix

    Reviewed by ch99q at 2022-04-11 08:52
  • 7. LocalStrategy not found

    Describe the bug

    Following the example code for Local Strategy, the import for LocalStrategy is missing or not found using import { Authenticator, LocalStrategy } from "remix-auth";

    https://github.com/sergiodxa/remix-auth/blob/68d3cf6e57859252b507f0d210a27f7935317a42/examples/local/app/services/auth.ts#L1

    Your Example Website or App

    examples/local

    Steps to Reproduce the Bug or Issue

    Install npm packages and try to run the app

    Expected behavior

    Compiler should not complain about missing import

    Screenshots or Videos

    Screen Shot 2022-01-22 at 3 18 55 PM

    Platform

    • OS: macOS
    • Browser: Chrome, Brave (but N/A)
    • Version: N/A

    Additional context

    No response

    Reviewed by hgeldenhuys at 2022-01-22 20:20
  • 8. createCookieSessionStorage not logged in

    Describe the bug

    I used OAuth2 After logging in, redirect (successRedirect) is processed in callback and null is returned in isAuthenticated. But when I use this code, it seems to work normally, so there seems to be a problem with createCookieSessionStorage.

    I used Google Translate, so the sentences may be weird.

    Your Example Website or App

    .

    Steps to Reproduce the Bug or Issue

    1. Log in
    2. Redirect to successRedirect
    3. isAuthenticated returned null

    Expected behavior

    When i use createCookieSessionStorage, isAuthenticated returned my profile.

    Screenshots or Videos

    No response

    Platform

    • Server OS: Linux
    • Browser: Chrome
    • Version: 97.0.4692.71

    Additional context

    No response

    Reviewed by B4TT3RY at 2022-01-22 15:26
  • 9. Add remix-auth-github and import GithubAuthStrategy from that package

    GithubStrategy is not coming from remix-auth but rather from remix-auth-github.

    This PR fixes the import.

    Also, the callback async (_, __, ___, profile) => login(profile.emails[0].value) is receiving data in the first parameter only (verified on my own project), the other 3 are undefined.

    Reviewed by vedovelli at 2022-01-19 19:05
  • 10. User is not logged in when verify data contains UTF-8 characters

    Describe the bug

    When the data returns from verify function contains UTF-8, cookie is set, authenticator redirects to successRedirect but user isn't logged in.

    Your Example Website or App

    https://github.com/thangngoc89/remix-auth/commit/73c6ed2c4b6fe723b2d67be248b780b1763bd2bd

    Steps to Reproduce the Bug or Issue

    1. Clone https://github.com/thangngoc89/remix-auth/
    2. git checkout utf8-bug
    3. cd examples/local
    4. npm run dev
    5. Go to http://localhost:3000
    6. Click on login
    7. Enter password abc123 and click login

    Expected behavior

    User should be logged in.

    Screenshots or Videos

    No response

    Platform

    • OS: [e.g. macOS, Windows, Linux]
    • Browser: [e.g. Chrome, Safari, Firefox]
    • Version: [e.g. 91.1]

    Additional context

    No response


    Update: I've tracked this error to this line https://github.com/remix-run/remix/blob/a69a631cb5add72d5fb24211ab2a0be367b6f2fd/packages/remix-server-runtime/cookies.ts#L156

    Since atob and btoa require that data must be Latin1 encoding, putting utf-8 data inside the cookie session would throw. I'm not sure why remix has silenced this error but I think this isn't a remix-auth's error

    Suggestions on how to fix this is available at https://stackoverflow.com/a/26603875

    Reviewed by thangngoc89 at 2021-12-30 04:59
  • 11. add bug report template

    What is the change?

    Add bug_report.yml file to enable Github's form based issue template

    • https://youtu.be/qQE1BUkf2-s?t=23

    Motivation

    • encourage's bug reporter's to put more care into their bug report before submission
    • this may help maintainer's receive more detailed & higher quality bug report's
    • adds helpful tips for user's during the process of creating a bug/issue report

    Demo of Change

    • https://github.com/cliffordfajardo/TEST-remix-auth-bug-template/issues/new/choose CleanShot 2021-12-12 at 17 51 01

    CleanShot 2021-12-12 at 17 54 16

    Reviewed by cliffordfajardo at 2021-12-13 01:51
  • 12. Pass custom error data

    This is more of an idea than a finalized PR - I'm not sure if this approach is the best one, and supporting it fully would require changes to the other community Strategies.

    I have been trying to get custom data passed through when throwing an error inside an authentication call (same as this discussion).

    Usually in these situations, I create a custom error class where I can add data to an error message:

    export class FancyError extends Error {
      constructor(message, data) {
        super(message);
        this.data = data;
      }
    }
    
    throw new FancyError('Uh oh', {moreData:'yes'})
    
    try{
      throw
    } catch(error) {
    console.log(error.message) // Uh oh
    console.log(error.data) // {moreData:'yes'}
    }
    

    The problem is that remix-auth and at least remix-auth-form pass errors around by calling new AuthorizationError(error.message) instead of passing through the original error object. This strips away any custom functionality.

    This PR adds a simple errors[] list to the AuthorizationError class, and allows passing an errors list into authenticate.failure.

    To actually make use of this, you'd have to throw new AuthorizationError(message,errors) inside your authentication call, and update your strategy to pass error.errors as the final parameter of failure.

    Here's remix-auth-form with that enabled:

    export class FormStrategy extends Strategy {
      name = 'form';
    
      async authenticate(request, sessionStorage, options) {
        let form = await request.formData();
    
        let user;
        try {
          user = await this.verify({ form, context: options.context });
        } catch (error) {
    
          let message = error.message;
          return await this.failure(
            message,
            request,
            sessionStorage,
            options,
            error.errors,
          );
        }
    
        return this.success(user, request, sessionStorage, options);
      }
    }
    

    And how I'd use it in the app:

    authenticator.use(
      new FormStrategy(async ({ form }) => {
        const api = new Api();
    
        let email = form.get('email');
        let password = form.get('password');
    
        try {
          return await api.login(email, password);
        } catch (error) {
          // this assumes my api is sending in error.data.errors - you could set any list of strings as the second argument
          throw new AuthorizationError(error.message, error.data?.errors);
        }
      }),
      'user-pass'
    );
    
    Reviewed by danieltott at 2022-05-26 20:08
  • 13. Accept session in addition to request

    Discussed in https://github.com/sergiodxa/remix-auth/discussions/142

    Originally posted by ngbrown March 12, 2022 isAuthenticated and logout take a Request just to get the session. The page very well could have already gotten a session and if it is a storage based session, then it would be advantageous to not fetch from the database multiple times.

    My suggestion is to change the signature like this:

      async logout(
        request: Request | Session,
        options: { redirectTo: string }
      ): Promise<void> {
        const session = isSession(request)
          ? request
          : await this._sessionStorage.getSession(request.headers.get("Cookie"));
    // the rest is identical ...
    }
    

    isSession is provided by remix.

    Reviewed by sergiodxa at 2022-03-31 18:36
  • 14. Accept session in addition to request

    Discussed in https://github.com/sergiodxa/remix-auth/discussions/142

    Originally posted by ngbrown March 12, 2022 isAuthenticated and logout take a Request just to get the session. The page very well could have already gotten a session and if it is a storage based session, then it would be advantageous to not fetch from the database multiple times.

    My suggestion is to change the signature like this:

      async logout(
        request: Request | Session,
        options: { redirectTo: string }
      ): Promise<void> {
        const session = isSession(request)
          ? request
          : await this._sessionStorage.getSession(request.headers.get("Cookie"));
    // the rest is identical ...
    }
    

    isSession is provided by remix.

    Reviewed by sergiodxa at 2022-03-14 21:13
  • 15. Support customizing OAuth strategy callback url dynamically

    This is similar to #35, but I make use of Netlify's preview deployments which can be accessed via several different endpoints. I don't actually know which endpoint a user has hit until the request is in flight. I'd like to be able to customize the callback url of the OAuth strategy based on the contents of the request (in this specific case I'm using Auth0). I can submit a pull request if desired.

    Reviewed by gregbty at 2021-11-24 03:53
  • 16. Identity Provider

    Remix Auth could not only give you a way to authenticate users against a third party service using oAuth2, it could also give you everything to become a identity provider using oAuth2 yourself, allowing you to expose the required endpoints for someone to login with your app.

    Reviewed by sergiodxa at 2021-11-20 18:19
  • 17. Better Docs

    • [ ] Documentation website
    • [ ] Document how to approach a Passport to Remix Auth migration
    • [ ] Document all current strategies
    • [ ] Create example apps for every strategy
    • [ ] Create examples using Cloudflare Workers
    Reviewed by sergiodxa at 2021-11-20 18:14
Email Link Strategy With Remix Auth

Email Link Strategy - Remix Auth This strategy is heavily based on kcd strategy

Jun 13, 2022
A collection of social media strategies for remix-auth

Remix Auth Socials A collection of Remix Auth strategies for Oauth2 Social login

Jun 7, 2022
The Remix Stack for deploying to AWS with DynamoDB, authentication, testing, linting, formatting, etc.
The Remix Stack for deploying to AWS with DynamoDB, authentication, testing, linting, formatting, etc.

The Remix Stack for deploying to AWS with DynamoDB, authentication, testing, linting, formatting, etc.

Jun 18, 2022
The Remix Stack for deploying to Fly with SQLite, authentication, testing, linting, formatting, etc.
The Remix Stack for deploying to Fly with SQLite, authentication, testing, linting, formatting, etc.

The Remix Stack for deploying to Fly with SQLite, authentication, testing, linting, formatting, etc.

Jun 20, 2022
Remix-code-snippets - Speed up your Remix development with dynamic code snippets
Remix-code-snippets - Speed up your Remix development with dynamic code snippets

remix-code-snippets Speed up your Remix development with dynamic code snippets.

Dec 29, 2021
Project Remix Blog - A Simple blog built with Remix (A Full Stack React Framework) and Prisma DB

Project Remix Blog - A Simple blog built with Remix (A Full Stack React Framework) and Prisma DB

Jan 2, 2022
Remix Fundamentals: Get a jumpstart on Remix with this workshop

?? Remix Fundamentals Build Better websites with Remix Remix enables you to buil

Jun 8, 2022
Remix-graphql - Utilities for using GraphQL with a Remix app

remix-graphql Remix and GraphQL can live together in harmony ❤️ This package con

Jun 17, 2022
Remix-cloudflare-prisma - An example of prisma working on cloudflare pages with Remix
Remix-cloudflare-prisma - An example of prisma working on cloudflare pages with Remix

Remix Cloudflare Prisma Example Developed by Jacob Paris Remix Docs Cloudflare P

Jun 2, 2022
Remix-seo - Collection of SEO utilities like sitemap, robots.txt, etc. for a Remix Application

Remix SEO Collection of SEO utilities like sitemap, robots.txt, etc. for a Remix

Apr 19, 2022
Online React Editor and IDE: compile, run, and host React apps
Online React Editor and IDE: compile, run, and host React apps

react-add-subtract Create a simple React application and learn how to manage the state and render JSX You need to provide an input which accept number

Oct 25, 2021
Run multiple JavaScript code snippets independently on a single page.
Run multiple JavaScript code snippets independently on a single page.

CodeSnippet.Run Run multiple JavaScript code snippets independently on a single page. Website CodeSnippet.Run Progress Create snippet Save snippet Del

Mar 29, 2022
A full Realtime Chat Application with Social Auth and dedicated chat APIs/sockets.
A full Realtime Chat Application with Social Auth and dedicated chat APIs/sockets.

Iced-Chat A full Realtime Chat Application with Social Auth and dedicated chat APIs/sockets. It uses Firebase(from google) for auth. It also uses Chat

Feb 28, 2022
React SPA App with Serverless Backend and Congito Auth Demo
React SPA App with Serverless Backend and Congito Auth Demo

React SPA App with Serverless Backend and Congito Auth Demo Overview This samples shows how to make a SPA application with serverless backend by AWS C

Jun 17, 2022
Aplicação desenvolvida com Next-js, Strepi, FaunaDB, Next-Auth e Prismic no curso de React-Js da RocketSeat

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Dec 24, 2021
A React Realtime Chat Application with Social Auth and dedicated chat APIs/sockets.
A React Realtime Chat Application with Social Auth and dedicated chat APIs/sockets.

Chat-Room This is a fully responsive React chat application with social authentication including Google and Facebook using Firebase online statuses, g

May 23, 2022
Papercups is an open source live customer chat web app written in Elixir
Papercups is an open source live customer chat web app written in Elixir

Papercups is an open source live customer chat web app written in Elixir

Jun 21, 2022
🍞 Jam is your own open source Clubhouse for mini conferences, friends, communities
🍞 Jam is your own open source Clubhouse for mini conferences, friends, communities

Jam ?? Jam is an open source alternative to Clubhouse, Twitter Spaces and similar audio spaces. With Jam you can create audio rooms that can be used f

Jun 25, 2022
Chroma is an open source design system from the team at LifeOmic built with React and TypeScript.
Chroma is an open source design system from the team at LifeOmic built with React and TypeScript.

Chroma is an open source design system from the team at LifeOmic. It is built with React and TypeScript. The goal of Chroma is to provide design-approved components to developers to speed up their development process and build visual consistency throughout web applications.

Jun 6, 2022