Files
supabase/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx
3dffdefd6e fix(docs) Resolve 196 mdx lint warnings for just, quickly, actually, PostgreSQL (#47358)
Closes DOCS-1057
Contributes to DOCS-1052

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## Problem

We have hundreds of MDX lint warnings in our docs going against style
best practices.

## Solution

Remove and replace in context the following:

- PostgreSQL. There was only one. There was concern about exceptions,
but I found none.
- Just
- Quickly
- Actually

### What changed

Edits follow the [Google developer documentation style
guide](https://developers.google.com/style): concise, direct, active
voice. The flagged words were removed when the sentence still read well,
or replaced when meaning needed to be preserved.

### Common patterns

| Flagged word | Approach | Example |
|---|---|---|
| **just** (filler) | Removed | "you just installed" → "you installed" |
| **just** (limiting) | **only** | "just one row" → "only one row" |
| **just like** | **like** / **the same as** | "function just like
regular users" → "function like regular users" |
| **not just** | **not only** | "not just errors" → "not only errors" |
| **quickly** (performance) | **efficiently** or removed | "find rows
quickly" → "find rows efficiently" |
| **quickly** (time) | **soon** / **rapidly** / removed | "expires too
quickly" → "expires too soon" |
| **actually** (filler) | Removed | "actually execute" → "execute"; "is
actually the most common" → "is the most common" |

## Tophatting

1. See the diff.
2. See that content continues to make sense in context.
3. Locally, `cd apps/docs` and run `pnpm run lint:mdx`.
4. Search for "just," "actually," "quickly", and "PostgreSQL" and see
there are 0 warnings.




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Updated wording across quickstarts, guides, and troubleshooting
articles for grammar, clarity, and consistent step-by-step phrasing.
* Clarified key concepts including Row Level Security policy evaluation
across Supabase products, deferred foreign key constraint behavior, and
when `EXPLAIN ANALYZE` executes queries (and related side effects).
* Refined several troubleshooting instructions and added guidance to cap
log payload size to reduce billed Logs Ingest volume.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nik Richers <nrichers@gmail.com>
Co-authored-by: Chris Chinchilla <chris.ward@supabase.io>
2026-06-29 09:40:25 -07:00

409 lines
14 KiB
Plaintext

---
title: 'Native Mobile Deep Linking'
subtitle: 'Set up Deep Linking for mobile applications.'
tocVideo: '8TZ6O1C8ujE'
---
Many Auth methods involve a redirect to your app. For example:
- Signup confirmation emails, Magic Link signins, and password reset emails contain a link that redirects to your app.
- In OAuth signins, an automatic redirect occurs to your app.
With Deep Linking, you can configure this redirect to open a specific page. This is necessary if, for example, you need to display a form for [password reset](/docs/guides/auth/passwords#resetting-a-users-password-forgot-password), or to manually exchange a token hash.
## Setting up deep linking
<Tabs
scrollable
size="large"
type="underlined"
defaultActiveId="react-native"
queryGroup="platform"
>
<TabPanel id="react-native" label="Expo React Native">
To link to your development build or standalone app, you need to specify a custom URL scheme for your app. You can register a scheme in your app config (app.json, app.config.js) by adding a string under the `scheme` key:
```json
{
"expo": {
"scheme": "com.supabase"
}
}
```
In your project's [auth settings](/dashboard/project/_/auth/url-configuration) add the redirect URL, e.g. `com.supabase://**`.
Finally, implement the OAuth and linking handlers. See the [supabase-js reference](/docs/reference/javascript/initializing?example=react-native-options-async-storage) for instructions on initializing the supabase-js client in React Native.
```tsx ./components/Auth.tsx
import { Button } from "react-native";
import { makeRedirectUri } from "expo-auth-session";
import * as QueryParams from "expo-auth-session/build/QueryParams";
import * as WebBrowser from "expo-web-browser";
import * as Linking from "expo-linking";
import { supabase } from "app/utils/supabase";
WebBrowser.maybeCompleteAuthSession(); // required for web only
const redirectTo = makeRedirectUri();
const createSessionFromUrl = async (url: string) => {
const { params, errorCode } = QueryParams.getQueryParams(url);
if (errorCode) throw new Error(errorCode);
const { access_token, refresh_token } = params;
if (!access_token) return;
const { data, error } = await supabase.auth.setSession({
access_token,
refresh_token,
});
if (error) throw error;
return data.session;
};
const performOAuth = async () => {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "github",
options: {
redirectTo,
skipBrowserRedirect: true,
},
});
if (error) throw error;
const res = await WebBrowser.openAuthSessionAsync(
data?.url ?? "",
redirectTo
);
if (res.type === "success") {
const { url } = res;
await createSessionFromUrl(url);
}
};
const sendMagicLink = async () => {
const { error } = await supabase.auth.signInWithOtp({
email: "valid.email@supabase.io",
options: {
emailRedirectTo: redirectTo,
},
});
if (error) throw error;
// Email sent.
};
export default function Auth() {
// Handle linking into app from email app.
const url = Linking.useLinkingURL();
if (url) createSessionFromUrl(url);
return (
<>
<Button onPress={performOAuth} title="Sign in with GitHub" />
<Button onPress={sendMagicLink} title="Send Magic Link" />
</>
);
}
```
For the best user experience it is recommended to use universal links which require a more elaborate setup. You can find the detailed setup instructions in the [Expo docs](https://docs.expo.dev/guides/deep-linking/).
</TabPanel>
<TabPanel id="flutter" label="Flutter">
// Currently supabase_flutter supports deep links on Android, iOS, Web, macOS and Windows.
### Deep link config
- Go to your [auth settings](/dashboard/project/_/auth/url-configuration) page.
- You need to enter your app redirect callback on `Additional Redirect URLs` field.
The redirect callback URL should have this format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.flutterquickstart://login-callback` is an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used.
![Supabase console deep link setting](/docs/img/deeplink-setting.png)
### Platform specific config
<Tabs
scrollable
size="large"
type="underlined"
defaultActiveId="android"
queryGroup="os"
>
<TabPanel id="android" label="Android">
```xml
<manifest ...>
<!-- ... other tags -->
<application ...>
<activity ...>
<!-- ... other tags -->
<!-- Deep Links -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="YOUR_SCHEME"
android:host="YOUR_HOSTNAME" />
</intent-filter>
</activity>
</application>
</manifest>
```
The `android:host` attribute is optional for Deep Links.
For more info: https://developer.android.com/training/app-links/deep-linking
</TabPanel>
<TabPanel id="ios" label="iOS">
For **Custom URL schemes** you need to declare the scheme in
`ios/Runner/Info.plist` (or through Xcode's Target Info editor,
under URL Types):
```xml
<!-- ... other tags -->
<plist>
<dict>
<!-- ... other tags -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>[YOUR_SCHEME]</string>
</array>
</dict>
</array>
<!-- ... other tags -->
</dict>
</plist>
```
For more info: https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
<$Partial path="universal_links_apple.mdx" />
</TabPanel>
<TabPanel id="windows" label="Windows">
Setting up deep links in Windows has few more steps than other platforms.
[Learn more](https://pub.dev/packages/app_links#windows)
Declare this method in `<PROJECT_DIR>\windows\runner\win32_window.h`
```cpp
// Dispatches link if any.
// This method enables our app to be with a single instance too.
// This is optional but mandatory if you want to catch further links in same app.
bool SendAppLinkToInstance(const std::wstring& title);
```
Add this inclusion at the top of `<PROJECT_DIR>\windows\runner\win32_window.cpp`
```cpp
#include "app_links_windows/app_links_windows_plugin.h"
```
Add this method in `<PROJECT_DIR>\windows\runner\win32_window.cpp`
```cpp
bool Win32Window::SendAppLinkToInstance(const std::wstring& title) {
// Find our exact window
HWND hwnd = ::FindWindow(kWindowClassName, title.c_str());
if (hwnd) {
// Dispatch new link to current window
SendAppLink(hwnd);
// (Optional) Restore our window to front in same state
WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) };
GetWindowPlacement(hwnd, &place);
switch(place.showCmd) {
case SW_SHOWMAXIMIZED:
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
break;
case SW_SHOWMINIMIZED:
ShowWindow(hwnd, SW_RESTORE);
break;
default:
ShowWindow(hwnd, SW_NORMAL);
break;
}
SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
SetForegroundWindow(hwnd);
// END Restore
// Window has been found, don't create another one.
return true;
}
return false;
}
```
Add the call to the previous method in `CreateAndShow`
```cpp
bool Win32Window::CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size) {
if (SendAppLinkToInstance(title)) {
return false;
}
...
```
At this point, you can register your own scheme.
On Windows, URL protocols are setup in the Windows registry.
This package won't do it for you.
You can achieve it with [url_protocol](https://pub.dev/packages/url_protocol) inside you app.
The most relevant solution is to include those registry modifications into your installer to allow for deregistration.
</TabPanel>
<TabPanel id="macos" label="macOS">
Add this XML chapter in your `macos/Runner/Info.plist` inside `<plist version="1.0"><dict>` chapter:
```xml
<!-- ... other tags -->
<plist version="1.0">
<dict>
<!-- ... other tags -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<!-- abstract name for this URL type (you can leave it blank) -->
<string>sample_name</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- your schemes -->
<string>sample</string>
</array>
</dict>
</array>
<!-- ... other tags -->
</dict>
</plist>
```
</TabPanel>
</Tabs>
</TabPanel>
<$Show if="sdk:swift">
<TabPanel id="swift" label="Swift">
### Deep link config
1. Go to your [auth settings](/dashboard/project/_/auth/url-configuration) page.
2. Enter your app redirect URL in the `Additional Redirect URLs` field. This is the URL that the user gets redirected to after clicking a magic link.
The redirect callback URL should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used.
![Supabase console deep link setting](/docs/img/deeplink-setting.png)
Now add a custom URL to your application, so the OS knows how to redirect back your application once the user clicks the magic link.
You have the option to use Xcode's Target Info Editor following [official Apple documentation](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app#Register-your-URL-scheme).
Or, declare the URL scheme manually in your `Info.plist` file.
```xml Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- other tags -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>io.supabase.user-management</string>
</array>
</dict>
</array>
</dict>
</plist>
```
<$Partial path="universal_links_apple.mdx" />
</TabPanel>
</$Show>
<$Show if="sdk:kotlin">
<TabPanel id="kotlin" label="Android Kotlin">
### Deep link config
1. Go to your [auth settings](/dashboard/project/_/auth/url-configuration) page.
2. Enter your app redirect URL in the `Additional Redirect URLs` field. This is the URL that the user gets redirected to after clicking a magic link.
The redirect callback URL must have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. For example: `io.supabase.user-management://login-callback`. You can use any values for `YOUR_SCHEME` and `YOUR_HOSTNAME`, but the scheme must be unique across the user's device. For this reason, a reverse domain of your website is typically used.
Now, edit the Android manifest to make sure the app opens when the user clicks on the magic link.
```xml
<manifest ...>
<!-- ... other tags -->
<application ...>
<activity ...>
<!-- ... other tags -->
<!-- Deep Links -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="YOUR_SCHEME"
android:host="YOUR_HOSTNAME" />
</intent-filter>
</activity>
</application>
</manifest>
```
Check the [Android documentation](https://developer.android.com/training/app-links/deep-linking) for more information.
Next, specify the scheme and host in the Supabase Client:
```kotlin
install(Auth) {
host = "login-callback"
scheme = "io.supabase.user-management"
}
```
Finally, call `Auth#handleDeeplinks` when the app opens:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supabase.handleDeeplinks(intent)
}
```
The user will now be authenticated when your app receives a valid deep link!
</TabPanel>
</$Show>
</Tabs>