Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/design-system/content/docs/icons.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function app() {
}
```

**Default props**: All icons default to `size={24}`. Stroke and fill defaults come from the source SVG's root attributes (e.g. `stroke-width="1"` on the root `<svg>` becomes the component's default `strokeWidth`). You can override those defaults at the call site, but any `stroke`, `fill`, or `stroke-width` set on individual child paths will still win.
**Default props**: All icons default to `size={24}`. Stroke icons use `strokeWidth={1.5}` and fill-only icons use `stroke="none"`. These defaults come from the source SVG's root attributes and can be intentionally overridden at the call site.

### Adding new custom icons

Expand All @@ -54,15 +54,15 @@ Follow these steps to add a new custom icon to the Supabase icon library.
1. **Create SVG file**: Add your SVG file to `packages/icons/src/raw-icons/` with a kebab-case name (e.g., `my-new-icon.svg`). Make sure it follows these requirements:
- Exported at 24×24px with `viewBox="0 0 24 24"`
- Uses `stroke="currentColor"` for strokes (no hardcoded colors)
- Uses `stroke-width="1.5"` (deviate based on optical weight if necessary)
- Uses `stroke-width="1.5"`
- Uses `fill="none"` for fills (no hardcoded colors)
- Icon content is optically centered and around 18×18px within the 24×24 frame
- Any unnecessary elements like `<clipPath>`, `<defs>`, and `<g>` wrappers have been removed
- SVG structure is as simple as possible with just `<path>` elements

For **fill-only icons** (e.g. logos that use shapes instead of strokes), add `stroke="none"` to the root `<svg>` element. The build will propagate this so the component never renders an unwanted stroke.

Prefer putting shared styling like `fill`, `stroke`, `stroke-width`, `stroke-linecap`, and `stroke-linejoin` on the root `<svg>`. The build propagates those root attributes as the component's defaults, and also converts them to camel-case for React compatibility (e.g. `strokeWidth`). If you put those attributes on individual child paths instead, they become fixed path-level styling and will override props passed to the component.
Put shared styling like `fill`, `stroke`, `stroke-width`, `stroke-linecap`, and `stroke-linejoin` on the root `<svg>`. The build validates this contract, propagates the root attributes as the component's defaults, and converts them to camel-case for React compatibility (e.g. `strokeWidth`). Child-level stroke styling is rejected because it would override props passed to the component.

2. **Build the component**: Run `npm run build:icons` from inside the `packages/icons` directory

Expand Down Expand Up @@ -132,4 +132,4 @@ Note `stroke="none"` on the root to prevent unwanted strokes, and `fill="current

### Troubleshooting

If your SVG specifies `stroke-width` attributes on individual child paths, they will override the component's `strokeWidth` prop. Keep shared stroke attributes on the root `<svg>` and remove them from individual paths if you want consumers to control stroke weight.
The icon build reports the source file and invalid attribute when an SVG does not follow the stroke or fill-only contract. Run `pnpm validate:icons` from `packages/icons` to check sources without regenerating components.
10 changes: 10 additions & 0 deletions apps/docs/components/FrameworkQuickstarts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ const frameworks = [
icon: '/docs/img/icons/nuxt-icon',
href: '/guides/getting-started/quickstarts/nuxtjs',
},
{
name: 'SvelteKit',
icon: '/docs/img/icons/svelte-icon',
href: '/guides/getting-started/quickstarts/sveltekit',
},
{
name: 'Hono',
icon: '/docs/img/icons/hono-icon',
href: '/guides/getting-started/quickstarts/hono',
},
{
name: 'iOS Swift',
icon: '/docs/img/icons/swift-icon-orange',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2621,6 +2621,7 @@ export const security: NavMenuConstant = {
items: [
{ name: 'SOC 2', url: '/guides/security/soc-2-compliance' },
{ name: 'HIPAA', url: '/guides/security/hipaa-compliance' },
{ name: 'GDPR', url: '/guides/security/gdpr-compliance' },
],
},
{
Expand Down Expand Up @@ -3085,6 +3086,7 @@ export const self_hosting: NavMenuConstant = {
name: 'Add Reverse Proxy with HTTPS',
url: '/guides/self-hosting/self-hosted-proxy-https',
},
{ name: 'Update your deployment', url: '/guides/self-hosting/updating' },
{
name: 'How-to Guides',
items: [
Expand All @@ -3098,7 +3100,7 @@ export const self_hosting: NavMenuConstant = {
{ name: 'Configure SAML 2.0 SSO', url: '/guides/self-hosting/self-hosted-saml-sso' },
{ name: 'Enable MCP server', url: '/guides/self-hosting/enable-mcp' },
{
name: 'Custom Postgres Extensions',
name: 'Build Custom Extensions',
url: '/guides/self-hosting/custom-postgres-extensions',
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
<Price price="0.50" /> per GB. You are only charged for usage exceeding your subscription plan's
quota.

| Plan | Quota | Over-Usage per GB |
| ---------- | ------ | ---------------------- |
| Free | 5 GB | - |
| Pro | 5 GB | <Price price="0.50" /> |
| Team | 5 GB | <Price price="0.50" /> |
| Enterprise | Custom | Custom |
Pricing details and included quotas will be published here when billing enforcement goes live.
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
<Price price="0.002" /> per GB. You are only charged for usage exceeding your subscription plan's
quota.

| Plan | Quota | Over-Usage per GB |
| ---------- | -------- | ----------------------- |
| Free | 1,000 GB | - |
| Pro | 1,000 GB | <Price price="0.002" /> |
| Team | 1,000 GB | <Price price="0.002" /> |
| Enterprise | Custom | Custom |
Pricing details and included quotas will be published here when billing enforcement goes live.
11 changes: 11 additions & 0 deletions apps/docs/content/_partials/quickstart_create_project.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 1. Create a Supabase project

To start, you need a Supabase project.

Create a new Supabase project from [the Dashboard of any organization](/dashboard/new/_) you belong to.

<Admonition type="note" title="Want to create a project programmatically?">

Use [the Management API](/docs/reference/api/v1-create-a-project) or ask [the MCP server](/docs/guides/ai-tools/mcp#account-management) to create a new Supabase project.

</Admonition>
12 changes: 1 addition & 11 deletions apps/docs/content/_partials/quickstart_db_setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,7 @@ on public.instruments
for select to anon
using (true);`

## 1. Create a Supabase project

To start, you need a Supabase project.

Create a new Supabase project from [the Dashboard of any organization](/dashboard/new/_) you belong to.

<Admonition type="note" title="Want to create a project programmatically?">

Use [the Management API](/docs/reference/api/v1-create-a-project) or ask [the MCP server](/docs/guides/ai-tools/mcp#account-management) to create a new Supabase project.

</Admonition>
<$Partial path="quickstart_create_project.mdx" />

## 2. Set up your database

Expand Down
14 changes: 14 additions & 0 deletions apps/docs/content/guides/api/creating-routes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ const { data: todos, error } = await supabase.from('todos').select('*')
```

</TabPanel>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

```c#
// Initialize the client
var supabase = new Supabase.Client(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY);
await supabase.InitializeAsync();

// Make a request
var todos = await supabase.From<Todo>().Get();
```

</TabPanel>
</$Show>
<TabPanel id="curl" label="cURL">

```bash
Expand Down
25 changes: 25 additions & 0 deletions apps/docs/content/guides/api/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,31 @@ let response = try await supabase
.order("score", ascending: false)
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

```c#
[Table("leaderboard")]
class Leaderboard : BaseModel
{
[PrimaryKey("id", false)]
public int Id { get; set; }

[Column("player")]
public string Player { get; set; }

[Column("score")]
public int Score { get; set; }
}

var result = await supabase
.From<Leaderboard>()
.Order(x => x.Score, Ordering.Descending)
.Get();
```

</TabPanel>
</$Show>
</Tabs>
18 changes: 18 additions & 0 deletions apps/docs/content/guides/api/using-custom-schemas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,24 @@ final data = await supabase.from('todos').select();
final data = await supabase.schema('myschema').from('todos').select();

````
</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

```c#
// Initialize the client with a custom schema
var supabase = new Supabase.Client(
SUPABASE_URL,
SUPABASE_PUBLISHABLE_KEY,
new SupabaseOptions { Schema = "myschema" }
);
await supabase.InitializeAsync();

// Make a request
var todos = await supabase.From<Todo>().Get();
```

</TabPanel>
</$Show>
<TabPanel id="curl" label="cURL">
Expand Down
40 changes: 40 additions & 0 deletions apps/docs/content/guides/auth/auth-anonymous.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ Call the [`sign_in_anonymously()`](/docs/reference/python/auth-signinanonymously
response = supabase.auth.sign_in_anonymously()
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

Call the [`SignInAnonymously()`](/docs/reference/csharp/sign-in-anonymously) method:

```c#
var session = await supabase.Auth.SignInAnonymously();
```

</TabPanel>
</$Show>
</Tabs>
Expand Down Expand Up @@ -204,6 +215,23 @@ response = supabase.auth.update_user({
})
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

You can use the [`Update()`](/docs/reference/csharp/update-user) method to link an email or phone identity to the anonymous user. To add a password for the anonymous user, the user's email or phone number needs to be verified first.

```c#
var updateEmail = await supabase.Auth.Update(new UserAttributes { Email = "valid.email@supabase.io" });

// verify the user's email by clicking on the email change link
// or entering the 6-digit OTP sent to the email address

// once the user has been verified, update the password
var updatePassword = await supabase.Auth.Update(new UserAttributes { Password = "password" });
```

</TabPanel>
</$Show>
</Tabs>
Expand Down Expand Up @@ -273,6 +301,18 @@ You can use the [`link_identity()`](/docs/reference/python/auth-linkidentity) me
response = supabase.auth.link_identity({'provider': 'google'})
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

You can use the [`LinkIdentity()`](/docs/reference/csharp/link-identity) method to link an OAuth identity to the anonymous user.

```c#
var state = await supabase.Auth.LinkIdentity(Provider.Google, new SignInOptions { FlowType = OAuthFlowType.PKCE });
var authorizeUrl = state.Uri;
```

</TabPanel>
</$Show>
</Tabs>
Expand Down
28 changes: 28 additions & 0 deletions apps/docs/content/guides/auth/auth-email-passwordless.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ response = supabase.auth.sign_in_with_otp({
})
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

```c#
var options = new SignInOptions { RedirectTo = "https://example.com/welcome" };
var didSendMagicLink = await supabase.Auth.SendMagicLink("valid.email@supabase.io", options);
```

</TabPanel>
</$Show>
</Tabs>
Expand Down Expand Up @@ -274,6 +284,15 @@ response = supabase.auth.sign_in_with_otp({
})
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

```c#
await supabase.Auth.SendMagicLink("valid.email@supabase.io");
```

</TabPanel>
</$Show>
</Tabs>
Expand Down Expand Up @@ -355,6 +374,15 @@ response = supabase.auth.verify_otp({
})
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

```c#
var session = await supabase.Auth.VerifyOTP("email@example.com", "123456", EmailOtpType.Email);
```

</TabPanel>
</$Show>
</Tabs>
Expand Down
30 changes: 30 additions & 0 deletions apps/docs/content/guides/auth/auth-identity-linking.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ Supabase Auth allows a user to initiate identity linking with a different email
response = supabase.auth.link_identity({'provider': 'google'})
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

Supabase Auth allows a user to initiate identity linking with a different email address when they are logged in. To link an OAuth identity to the user, call [`LinkIdentity()`](/docs/reference/csharp/link-identity):

```c#
var state = await supabase.Auth.LinkIdentity(Provider.Google, new SignInOptions { FlowType = OAuthFlowType.PKCE });
var authorizeUrl = state.Uri;
```

</TabPanel>
</$Show>
</Tabs>
Expand Down Expand Up @@ -258,6 +270,24 @@ if google_identity:
response = supabase.auth.unlink_identity(google_identity.identity_id)
```

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

Use `CurrentUser.Identities` to get all the identities linked to a user. Then, call [`UnlinkIdentity()`](/docs/reference/csharp/unlink-identity) to unlink the identity. The user needs to be logged in and have at least 2 linked identities in order to unlink an existing identity.

```c#
// get all identities linked to the user
var identities = supabase.Auth.CurrentUser.Identities;

// find the google identity linked to the user
var googleIdentity = identities.First(x => x.Provider == "google");

// unlink the google identity from the user
await supabase.Auth.UnlinkIdentity(googleIdentity);
```

</TabPanel>
</$Show>
</Tabs>
Expand Down
11 changes: 11 additions & 0 deletions apps/docs/content/guides/auth/debugging/error-codes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ Rest exceptions are split into a few classes:

All instances and subclasses of a `AuthRestException` have a `errorCode` property that can be used to identify the error returned by the server.

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">
All exceptions originating from the `supabase.Auth` namespace of the C# client library are wrapped by the `GotrueException` class.

`GotrueException` exposes:

- `StatusCode` -- the HTTP status code returned by the Supabase Auth API.
- `Reason` -- a `FailureHint.Reason` value that categorizes the error, so you can branch on it without parsing the message.

</TabPanel>
</$Show>

Expand Down
12 changes: 12 additions & 0 deletions apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,18 @@ supabase.auth.signInWith(SSO) {

Calling [`signInWith(SSO)`](/docs/reference/kotlin/auth-signinwithsso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead.

</TabPanel>
</$Show>
<$Show if="sdk:csharp">
<TabPanel id="csharp" label="C#">

```c#
var response = await supabase.Auth.SignInWithSSO("company.com");
var ssoUrl = response.Uri;
```

Calling [`SignInWithSSO`](/docs/reference/csharp/sign-in-with-sso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead.

</TabPanel>
</$Show>
</Tabs>
Expand Down
Loading
Loading