Docs/UI library vue nuxt clients (#38279)

* docs: add vue client to ui-library

* docs: add missing vue client to llms.txt

* docs: add nuxt client to ui-library

* docs: wrong env variable names

* docs: fix dependencies

* docs: update client-nuxtjs.json

* Reinstall the deps so that the pnpm-lock.yaml has less changes.

* Add blocks/vue package.

* Remove the vue blocks from ui-library.

* Copy the vue blocks into ui-library.

* Clean up unneeded files.

* Regenerate the pnpm-lock file from master.

* Fix prettier errors.

* docs: update shadcn-vue cli

* docs: reusable server client

* Small things

* docs: improvments after CR

---------

Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
Co-authored-by: Terry Sutton <saltcod@gmail.com>
This commit is contained in:
Jakub Andrzejewski
2025-09-26 14:48:47 +02:00
committed by GitHub
parent 5f533247e1
commit 99499164dc
33 changed files with 4778 additions and 281 deletions

View File

@@ -12,9 +12,11 @@ interface BlockItemProps {
}
export const BlockItem = ({ name }: BlockItemProps) => {
const framework = name.includes('vue') || name.includes('nuxtjs') ? 'vue' : 'react'
return (
<div className="mt-4">
<Command name={name} highlight />
<Command name={name} highlight framework={framework} />
<OpenInV0Button name={name} className="w-fit shrink-0 mt-4" />
</div>
)

View File

@@ -8,13 +8,15 @@ import { useLocalStorage } from './use-local-storage'
interface CommandCopyProps {
name: string
highlight?: boolean
// For Vue, we need to use the `shadcn-vue` package instead of `shadcn`
framework?: 'react' | 'vue'
}
type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'
const LOCAL_STORAGE_KEY = 'package-manager-copy-command'
export function Command({ name, highlight }: CommandCopyProps) {
export function Command({ name, highlight, framework = 'react' }: CommandCopyProps) {
const [value, setValue] = useLocalStorage(LOCAL_STORAGE_KEY, 'npm')
const getBaseUrl = () => {
@@ -30,12 +32,27 @@ export function Command({ name, highlight }: CommandCopyProps) {
const baseUrl = getBaseUrl()
const componentPath = `${process.env.NEXT_PUBLIC_BASE_PATH ?? ''}/r/${name}.json`
const commands: Record<PackageManager, string> = {
npm: `npx shadcn@latest add ${baseUrl}${componentPath}`,
pnpm: `pnpm dlx shadcn@latest add ${baseUrl}${componentPath}`,
yarn: `yarn dlx shadcn@latest add ${baseUrl}${componentPath}`,
bun: `bunx --bun shadcn@latest add ${baseUrl}${componentPath}`,
}
const commands: Record<PackageManager, string> =
framework === 'react'
? {
npm: `npx shadcn@latest add ${baseUrl}${componentPath}`,
pnpm: `pnpm dlx shadcn@latest add ${baseUrl}${componentPath}`,
yarn: `yarn dlx shadcn@latest add ${baseUrl}${componentPath}`,
bun: `bunx --bun shadcn@latest add ${baseUrl}${componentPath}`,
}
: framework === 'vue'
? {
npm: `npx shadcn-vue@latest add ${baseUrl}${componentPath}`,
pnpm: `pnpm dlx shadcn-vue@latest add ${baseUrl}${componentPath}`,
yarn: `yarn dlx shadcn-vue@latest add ${baseUrl}${componentPath}`,
bun: `bunx --bun shadcn-vue@latest add ${baseUrl}${componentPath}`,
}
: {
npm: `npx shadcn@latest add ${baseUrl}${componentPath}`,
pnpm: `pnpm dlx shadcn@latest add ${baseUrl}${componentPath}`,
yarn: `yarn dlx shadcn@latest add ${baseUrl}${componentPath}`,
bun: `bunx --bun shadcn@latest add ${baseUrl}${componentPath}`,
}
return (
<Tabs_Shadcn_ value={value} onValueChange={setValue} className="w-full">

View File

@@ -54,7 +54,7 @@ export const componentPages: SidebarNavGroup = {
items: [
{
title: 'Client',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/client',
items: [],
commandItemLabel: 'Supabase Client',
@@ -141,4 +141,6 @@ export const frameworkTitles: Record<string, string> = {
'react-router': 'React Router',
tanstack: 'TanStack Start',
react: 'React SPA',
vue: 'Vue',
nuxtjs: 'Nuxt.js',
}

View File

@@ -0,0 +1,44 @@
---
title: Supabase Client Libraries
description: Supabase client for Nuxt.js
---
## Installation
<BlockItem name="supabase-client-nuxtjs" description="Supabase Client for Nuxt.js" />
## Folder structure
<RegistryBlock itemName="supabase-client-nuxtjs" />
## Usage
This block installs a Supabase client for connecting your Nuxt.js project to Supabase. It's designed to fully supports server-side rendering (SSR).
If you've already set up your Supabase client—either using the `npm create nuxt@latest` template or another method—you can continue using your existing setup.
### Getting started
After installing the block, you'll have the following environment variables in your `.env.local` file:
```env
NUXT_PUBLIC_SUPABASE_URL=
NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY=
```
- If you're using supabase.com, you can find these values in the [Connect modal](https://supabase.com/dashboard/project/_?showConnect=true&tab=frameworks&framework=vuejs&using=supabasejs) under App Frameworks or in your project's [API keys](https://supabase.com/dashboard/project/_/settings/api-keys).
- If you're using a local instance of Supabase, you can find these values by running `supabase start` or `supabase status` (if you already have it running).
- Nuxt recommends [NuxtSupabase](https://supabase.nuxtjs.org/) module to integrate Nuxt application with Supabase. Its an alternative to this approach, but both approaches are fine.
<Callout type="warning" className="mt-4">
{' '}
This Supabase client is built for SSR with the Nuxt.js. If you're building a Vue SPA, use the [Vue
SPA client](/ui/docs/vue/client) instead.{' '}
</Callout>
## Further reading
- [Use Supabase with Nuxt](https://supabase.com/docs/guides/getting-started/quickstarts/nuxtjs)
- [Build a User Management App with Nuxt 3](https://supabase.com/docs/guides/getting-started/tutorials/with-nuxt-3)

View File

@@ -0,0 +1,59 @@
---
title: Supabase Client Libraries
description: Supabase client for Vue Single Page Applications
---
## Installation
<BlockItem name="supabase-client-vue" description="Supabase Client for Vue SPA" />
## Folder structure
<RegistryBlock itemName="supabase-client-vue" />
## Usage
This block installs a Supabase client for connecting your Vue project to Supabase. It's designed for use in client-side components.
If you've already set up a Supabase client in your project, you can just continue using that existing setup.
### Getting started
After installing the block, you'll have the following environment variables in your `.env.local` file:
```env
VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY=
```
- If you're using supabase.com, you can find these values in the [Connect modal](https://supabase.com/dashboard/project/_?showConnect=true&tab=frameworks&framework=vuejs&using=supabasejs) under App Frameworks or in your project's [API keys](https://supabase.com/dashboard/project/_/settings/api-keys).
- If you're using a local instance of Supabase, you can find these values by running `supabase start` or `supabase status` (if you already have it running).
You can use the client in your Vue component like following:
```vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { createClient } from './lib/supabase/client'
const profile = ref(null)
onMounted(async () => {
const { data, error } = await createClient.from('profiles').select('*').single()
if (!error) profile.value = data
})
</script>
<template>
<div>
{{ profile }}
</div>
</template>
```
## Further reading
- [Generating TypeScript types for your client](https://supabase.com/docs/guides/api/rest/generating-types)
- [Use Supabase with Vue](https://supabase.com/docs/guides/getting-started/quickstarts/vue)

View File

@@ -7,7 +7,7 @@
"preinstall": "npx only-allow pnpm",
"dev": "next dev --port 3004",
"build": "pnpm run content:build && pnpm run build:registry && pnpm run build:llms && next build --turbopack",
"build:registry": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-registry.mts && prettier --cache --write registry.json && rimraf -G public/r && shadcn build && tsx scripts/clean-registry.ts",
"build:registry": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-registry.mts && prettier --cache --write registry.json && rimraf -G public/r && shadcn build && cp node_modules/@supabase/vue-blocks/public/r/* public/r && tsx scripts/clean-registry.ts",
"build:llms": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-llms-txt.ts",
"start": "next start",
"lint": "next lint",
@@ -50,6 +50,7 @@
"@supabase/postgrest-js": "*",
"@supabase/supa-mdx-lint": "0.2.6-alpha",
"@tanstack/react-query": "^5.83.0",
"@supabase/vue-blocks": "workspace:*",
"axios": "^1.11.0",
"class-variance-authority": "*",
"cmdk": "^1.0.0",

View File

@@ -81,3 +81,7 @@ Library of components for your project. The components integrate with Supabase a
- Real-time cursor sharing for collaborative applications
- [Social Authentication](https://supabase.com/ui/docs/tanstack/social-auth)
- Social authentication block for Tanstack Start
- [Supabase Client Libraries](https://supabase.com/ui/docs/vue/client)
- Supabase client for Vue Single Page Applications
- [Supabase Client Libraries](https://supabase.com/ui/docs/nuxtjs/client)
- Supabase client for Nuxt.js

View File

@@ -0,0 +1,42 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-nuxtjs",
"type": "registry:lib",
"title": "Supabase Client for Nuxt.js",
"description": "",
"dependencies": [
"@supabase/ssr@latest",
"@supabase/supabase-js@latest"
],
"registryDependencies": [],
"files": [
{
"path": "registry/default/clients/nuxtjs/lib/supabase/client.ts",
"content": "import { createBrowserClient } from '@supabase/ssr'\n\nexport function createClient() {\n return createBrowserClient(\n process.env.NUXT_PUBLIC_SUPABASE_URL!,\n process.env.NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY!\n )\n}\n",
"type": "registry:lib"
},
{
"path": "registry/default/clients/nuxtjs/server/middleware/is-authenticated.ts",
"content": "import { defineNuxtRouteMiddleware, navigateTo, useRequestEvent } from 'nuxt/app'\nimport { createSupabaseServerClient } from '../supabase/client'\n\nexport default defineNuxtRouteMiddleware(async (to) => {\n const event = useRequestEvent()\n\n // create Supabase SSR client directly here\n const supabase = createSupabaseServerClient(event);\n\n // check current user\n const { data: { user } } = await supabase.auth.getUser()\n\n if (!user && to.path !== '/login') {\n return navigateTo('/login')\n }\n})\n",
"type": "registry:file",
"target": "server/middleware/is-authenticated.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/api/profile.get.ts",
"content": "import { createError, defineEventHandler } from 'h3';\nimport { createSupabaseServerClient } from '../supabase/client';\n\nexport default defineEventHandler(async (event) => {\n // Create Supabase SSR client\n const supabase = createSupabaseServerClient(event)\n\n // Example: get user session\n const {\n data: { user },\n } = await supabase.auth.getUser();\n\n if (!user) {\n return { error: 'Not authenticated' };\n }\n\n // Fetch profile row\n const { data, error } = await supabase\n .from('profiles')\n .select('*')\n .eq('id', user.id)\n .single();\n\n if (error) {\n throw createError({ statusCode: 500, statusMessage: error.message });\n }\n\n return { profile: data };\n});\n",
"type": "registry:file",
"target": "server/api/profile.get.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/supabase/client.ts",
"content": "import { createServerClient } from '@supabase/ssr'\nimport { getCookie, setCookie, deleteCookie, H3Event, EventHandlerRequest } from 'h3'\n\nexport const createSupabaseServerClient = (event: H3Event<EventHandlerRequest> | undefined) => {\n return createServerClient(\n process.env.NUXT_PUBLIC_SUPABASE_URL!,\n process.env.NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY!,\n {\n cookies: {\n get: (key) => getCookie(event!, key),\n set: (key, value, options) => setCookie(event!, key, value, options),\n remove: (key, options) => deleteCookie(event!, key, options),\n },\n }\n )\n}",
"type": "registry:file",
"target": "server/supabase/client.ts"
}
],
"envVars": {
"NUXT_PUBLIC_SUPABASE_URL": "",
"NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"docs": "You'll need to set the following environment variables in your project: `NUXT_PUBLIC_SUPABASE_URL` and `NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY`."
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-vue",
"type": "registry:lib",
"title": "Supabase Client for Vue",
"description": "",
"dependencies": [
"@supabase/supabase-js@latest"
],
"registryDependencies": [],
"files": [
{
"path": "registry/default/clients/vue/lib/supabase/client.ts",
"content": "/// <reference types=\"vite/types/importMeta.d.ts\" />\nimport { createClient as createSupabaseClient } from '@supabase/supabase-js'\n\nexport function createClient() {\n return createSupabaseClient(\n import.meta.env.VITE_SUPABASE_URL!,\n import.meta.env.VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY!\n )\n}\n",
"type": "registry:lib"
}
],
"envVars": {
"VITE_SUPABASE_URL": "",
"VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"docs": "You'll need to set the following environment variables in your project: `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY`."
}

View File

@@ -1,4 +1,4 @@
type supportedFrameworks = 'nextjs' | 'react-router' | 'tanstack' | 'react'
type supportedFrameworks = 'nextjs' | 'react-router' | 'tanstack' | 'react' | 'vue' | 'nuxtjs'
export interface NavItem {
title: string
href?: string

38
blocks/vue/.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.contentlayer

View File

@@ -0,0 +1,33 @@
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"
export const Index: Record<string, any> = {
"default": {
"supabase-client-nuxtjs": {
name: "supabase-client-nuxtjs",
type: "registry:lib",
registryDependencies: [],
source: "",
files: ["registry/default/clients/nuxtjs/lib/supabase/client.ts","registry/default/clients/nuxtjs/lib/supabase/middleware.ts","registry/default/clients/nuxtjs/lib/supabase/server.ts"],
category: "undefined",
subcategory: "undefined",
chunks: []
}
,
"supabase-client-vue": {
name: "supabase-client-vue",
type: "registry:lib",
registryDependencies: [],
source: "",
files: ["registry/default/clients/vue/lib/supabase/client.ts"],
category: "undefined",
subcategory: "undefined",
chunks: []
}
},
}

View File

@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "gray",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/registry/default/components",
"utils": "@/lib/utils",
"ui": "@/registry/default/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@@ -0,0 +1,86 @@
import * as fs from 'fs'
export interface RegistryNode {
name: string
path: string
originalPath: string
type: 'directory' | 'file'
children?: RegistryNode[]
content?: string
}
interface RegistryFile {
path: string
target?: string
type: string
content: string
}
const DEFAULT_PATHS = {
component: '/components',
hook: '/hooks',
util: '/lib',
} as const
/**
* Converts a flat registry array into a hierarchical file tree structure
*/
export function generateRegistryTree(registryPath: string): RegistryNode[] {
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf-8')) as { files: RegistryFile[] }
const tree: RegistryNode[] = []
const sortedRegistry = [...registry.files].sort((a, b) => a.path.localeCompare(b.path))
for (const file of sortedRegistry) {
const itemPath = file.target || getDefaultPath(file)
const pathParts = itemPath.split('/').filter(Boolean)
let currentLevel = tree
for (let i = 0; i < pathParts.length; i++) {
const part = pathParts[i]
const isLast = i === pathParts.length - 1
const path = '/' + pathParts.slice(0, i + 1).join('/')
let node = currentLevel.find((n) => n.name === part)
// Remove any paths in the file content that point to the block directory.
const content = file.content
.replaceAll(/@\/registry\/default\/blocks\/.+?\//gi, '@/')
.replaceAll(/@\/registry\/default\/fixtures\//gi, '@/')
.replaceAll(/@\/registry\/default\//gi, '@/')
.replaceAll(/@\/clients\/.+?\//gi, '@/')
if (!node) {
node = {
name: part,
path,
originalPath: file.path,
type: isLast ? 'file' : 'directory',
...(isLast ? { content } : { children: [] }),
}
currentLevel.push(node)
}
if (!isLast) {
node.children = node.children || []
currentLevel = node.children
}
}
}
return tree
}
/**
* Determines the default path for an item based on its type
*/
function getDefaultPath(item: RegistryFile): string {
const type = item.type.toLowerCase() || ''
const basePath = DEFAULT_PATHS[type as keyof typeof DEFAULT_PATHS] || ''
// clean all paths that start with paths specific to this repo organization
const filePath = item.path
.replace(/registry\/default\/blocks\/.+?\//, '')
.replace(/registry\/default\/clients\/.+?\//, '')
return `${basePath}/${filePath}`
}

23
blocks/vue/package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "@supabase/vue-blocks",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"preinstall": "npx only-allow pnpm",
"build": "prettier --cache --write registry.json && rimraf -G public/r && shadcn build && tsx scripts/clean-registry.ts",
"clean": "rimraf node_modules .next .turbo",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"h3": "^1.15.4",
"nuxt": "^4.0.3",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.49.1"
},
"devDependencies": {
"shadcn": "^2.10.0",
"tsconfig": "workspace:*",
"vite": "catalog:"
}
}

View File

@@ -0,0 +1,42 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-nuxtjs",
"type": "registry:lib",
"title": "Supabase Client for Nuxt.js",
"description": "",
"dependencies": [
"@supabase/ssr@latest",
"@supabase/supabase-js@latest"
],
"registryDependencies": [],
"files": [
{
"path": "registry/default/clients/nuxtjs/lib/supabase/client.ts",
"content": "import { createBrowserClient } from '@supabase/ssr'\n\nexport function createClient() {\n return createBrowserClient(\n process.env.NUXT_PUBLIC_SUPABASE_URL!,\n process.env.NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY!\n )\n}\n",
"type": "registry:lib"
},
{
"path": "registry/default/clients/nuxtjs/server/middleware/is-authenticated.ts",
"content": "import { defineNuxtRouteMiddleware, navigateTo, useRequestEvent } from 'nuxt/app'\nimport { createSupabaseServerClient } from '../supabase/client'\n\nexport default defineNuxtRouteMiddleware(async (to) => {\n const event = useRequestEvent()\n\n // create Supabase SSR client directly here\n const supabase = createSupabaseServerClient(event);\n\n // check current user\n const { data: { user } } = await supabase.auth.getUser()\n\n if (!user && to.path !== '/login') {\n return navigateTo('/login')\n }\n})\n",
"type": "registry:file",
"target": "server/middleware/is-authenticated.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/api/profile.get.ts",
"content": "import { createError, defineEventHandler } from 'h3';\nimport { createSupabaseServerClient } from '../supabase/client';\n\nexport default defineEventHandler(async (event) => {\n // Create Supabase SSR client\n const supabase = createSupabaseServerClient(event)\n\n // Example: get user session\n const {\n data: { user },\n } = await supabase.auth.getUser();\n\n if (!user) {\n return { error: 'Not authenticated' };\n }\n\n // Fetch profile row\n const { data, error } = await supabase\n .from('profiles')\n .select('*')\n .eq('id', user.id)\n .single();\n\n if (error) {\n throw createError({ statusCode: 500, statusMessage: error.message });\n }\n\n return { profile: data };\n});\n",
"type": "registry:file",
"target": "server/api/profile.get.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/supabase/client.ts",
"content": "import { createServerClient } from '@supabase/ssr'\nimport { getCookie, setCookie, deleteCookie, H3Event, EventHandlerRequest } from 'h3'\n\nexport const createSupabaseServerClient = (event: H3Event<EventHandlerRequest> | undefined) => {\n return createServerClient(\n process.env.NUXT_PUBLIC_SUPABASE_URL!,\n process.env.NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY!,\n {\n cookies: {\n get: (key) => getCookie(event!, key),\n set: (key, value, options) => setCookie(event!, key, value, options),\n remove: (key, options) => deleteCookie(event!, key, options),\n },\n }\n )\n}",
"type": "registry:file",
"target": "server/supabase/client.ts"
}
],
"envVars": {
"NUXT_PUBLIC_SUPABASE_URL": "",
"NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"docs": "You'll need to set the following environment variables in your project: `NUXT_PUBLIC_SUPABASE_URL` and `NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY`."
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-vue",
"type": "registry:lib",
"title": "Supabase Client for Vue",
"description": "",
"dependencies": [
"@supabase/supabase-js@latest"
],
"registryDependencies": [],
"files": [
{
"path": "registry/default/clients/vue/lib/supabase/client.ts",
"content": "/// <reference types=\"vite/types/importMeta.d.ts\" />\nimport { createClient as createSupabaseClient } from '@supabase/supabase-js'\n\nexport function createClient() {\n return createSupabaseClient(\n import.meta.env.VITE_SUPABASE_URL!,\n import.meta.env.VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY!\n )\n}\n",
"type": "registry:lib"
}
],
"envVars": {
"VITE_SUPABASE_URL": "",
"VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"docs": "You'll need to set the following environment variables in your project: `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY`."
}

62
blocks/vue/registry.json Normal file
View File

@@ -0,0 +1,62 @@
{
"$schema": "https://ui.shadcn.com/schema/registry.json",
"name": "Supabase UI Library",
"homepage": "https://supabase.com/ui",
"items": [
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-nuxtjs",
"type": "registry:lib",
"title": "Supabase Client for Nuxt.js",
"description": "",
"registryDependencies": [],
"dependencies": ["@supabase/ssr@latest", "@supabase/supabase-js@latest"],
"docs": "You'll need to set the following environment variables in your project: `NUXT_PUBLIC_SUPABASE_URL` and `NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY`.",
"envVars": {
"NUXT_PUBLIC_SUPABASE_URL": "",
"NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"files": [
{
"path": "registry/default/clients/nuxtjs/lib/supabase/client.ts",
"type": "registry:lib"
},
{
"path": "registry/default/clients/nuxtjs/server/middleware/is-authenticated.ts",
"type": "registry:file",
"target": "server/middleware/is-authenticated.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/api/profile.get.ts",
"type": "registry:file",
"target": "server/api/profile.get.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/supabase/client.ts",
"type": "registry:file",
"target": "server/supabase/client.ts"
}
]
},
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-vue",
"type": "registry:lib",
"title": "Supabase Client for Vue",
"description": "",
"registryDependencies": [],
"dependencies": ["@supabase/supabase-js@latest"],
"docs": "You'll need to set the following environment variables in your project: `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY`.",
"envVars": {
"VITE_SUPABASE_URL": "",
"VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"files": [
{
"path": "registry/default/clients/vue/lib/supabase/client.ts",
"type": "registry:lib"
}
]
}
]
}

View File

@@ -0,0 +1,5 @@
import { type Registry } from 'shadcn/registry'
import nuxtjs from './default/clients/nuxtjs/registry-item.json' with { type: 'json' }
import vue from './default/clients/vue/registry-item.json' with { type: 'json' }
export const clients = [nuxtjs, vue] as Registry['items']

View File

@@ -0,0 +1,8 @@
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NUXT_PUBLIC_SUPABASE_URL!,
process.env.NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY!
)
}

View File

@@ -0,0 +1,35 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-nuxtjs",
"type": "registry:lib",
"title": "Supabase Client for Nuxt.js",
"description": "",
"registryDependencies": [],
"dependencies": ["@supabase/ssr@latest", "@supabase/supabase-js@latest"],
"docs": "You'll need to set the following environment variables in your project: `NUXT_PUBLIC_SUPABASE_URL` and `NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY`.",
"envVars": {
"NUXT_PUBLIC_SUPABASE_URL": "",
"NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"files": [
{
"path": "registry/default/clients/nuxtjs/lib/supabase/client.ts",
"type": "registry:lib"
},
{
"path": "registry/default/clients/nuxtjs/server/middleware/is-authenticated.ts",
"type": "registry:file",
"target": "server/middleware/is-authenticated.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/api/profile.get.ts",
"type": "registry:file",
"target": "server/api/profile.get.ts"
},
{
"path": "registry/default/clients/nuxtjs/server/supabase/client.ts",
"type": "registry:file",
"target": "server/supabase/client.ts"
}
]
}

View File

@@ -0,0 +1,29 @@
import { createError, defineEventHandler } from 'h3';
import { createSupabaseServerClient } from '../supabase/client';
export default defineEventHandler(async (event) => {
// Create Supabase SSR client
const supabase = createSupabaseServerClient(event)
// Example: get user session
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return { error: 'Not authenticated' };
}
// Fetch profile row
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
if (error) {
throw createError({ statusCode: 500, statusMessage: error.message });
}
return { profile: data };
});

View File

@@ -0,0 +1,16 @@
import { defineNuxtRouteMiddleware, navigateTo, useRequestEvent } from 'nuxt/app'
import { createSupabaseServerClient } from '../supabase/client'
export default defineNuxtRouteMiddleware(async (to) => {
const event = useRequestEvent()
// create Supabase SSR client directly here
const supabase = createSupabaseServerClient(event);
// check current user
const { data: { user } } = await supabase.auth.getUser()
if (!user && to.path !== '/login') {
return navigateTo('/login')
}
})

View File

@@ -0,0 +1,16 @@
import { createServerClient } from '@supabase/ssr'
import { getCookie, setCookie, deleteCookie, H3Event, EventHandlerRequest } from 'h3'
export const createSupabaseServerClient = (event: H3Event<EventHandlerRequest> | undefined) => {
return createServerClient(
process.env.NUXT_PUBLIC_SUPABASE_URL!,
process.env.NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY!,
{
cookies: {
get: (key) => getCookie(event!, key),
set: (key, value, options) => setCookie(event!, key, value, options),
remove: (key, options) => deleteCookie(event!, key, options),
},
}
)
}

View File

@@ -0,0 +1,9 @@
/// <reference types="vite/types/importMeta.d.ts" />
import { createClient as createSupabaseClient } from '@supabase/supabase-js'
export function createClient() {
return createSupabaseClient(
import.meta.env.VITE_SUPABASE_URL!,
import.meta.env.VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY!
)
}

View File

@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "supabase-client-vue",
"type": "registry:lib",
"title": "Supabase Client for Vue",
"description": "",
"registryDependencies": [],
"dependencies": ["@supabase/supabase-js@latest"],
"docs": "You'll need to set the following environment variables in your project: `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY`.",
"envVars": {
"VITE_SUPABASE_URL": "",
"VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY": ""
},
"files": [
{
"path": "registry/default/clients/vue/lib/supabase/client.ts",
"type": "registry:lib"
}
]
}

View File

@@ -0,0 +1,9 @@
import { type Registry } from 'shadcn/registry'
import { clients } from './clients'
export const registry = {
name: 'Supabase UI Library',
homepage: 'https://supabase.com/ui',
items: [...clients],
} satisfies Registry

View File

@@ -0,0 +1,55 @@
import * as fs from 'fs'
import * as path from 'path'
function processJsonFile(filePath: string) {
try {
// Read the file
const content = fs.readFileSync(filePath, 'utf8')
const json = JSON.parse(content)
// Convert to string to do replacement
let stringified = JSON.stringify(json, null, 2)
// Perform the replacement
stringified = stringified
.replace(/\/ui\/example\/password-based-auth/g, '')
.replace(/\/example\/password-based-auth/g, '')
.replaceAll(
"import { Link } from '@/registry/default/components/ui/link'",
"import Link from 'next/link'"
)
// Write back to file
fs.writeFileSync(filePath, stringified)
console.log(`✓ Updated ${filePath}`)
} catch (error) {
console.error(`Error processing ${filePath}:`, error)
}
}
function processDirectory(directoryPath: string) {
const files = fs.readdirSync(directoryPath)
files.forEach((file) => {
const fullPath = path.join(directoryPath, file)
const stat = fs.statSync(fullPath)
if (stat.isDirectory()) {
processDirectory(fullPath)
} else if (path.extname(file) === '.json') {
processJsonFile(fullPath)
}
})
}
// Start processing from the specified directory
const targetDir = path.join(process.cwd(), 'public/r')
if (!fs.existsSync(targetDir)) {
console.error('Target directory does not exist:', targetDir)
process.exit(1)
}
console.log('Starting JSON file processing...')
processDirectory(targetDir)
console.log('Processing complete!')

View File

@@ -0,0 +1,20 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"moduleResolution": "node",
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"strict": true
},
"exclude": ["node_modules"]
}

31
blocks/vue/tsconfig.json Normal file
View File

@@ -0,0 +1,31 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "tsconfig/base.json",
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
},
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules", "./scripts/build-registry.mts"]
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "es6",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"isolatedModules": false
},
"include": [".contentlayer/generated", "scripts/**/*.ts"],
"exclude": ["node_modules"]
}

4248
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
packages:
- 'apps/*'
- 'packages/*'
- 'blocks/*'
- 'e2e/*'
catalog: