OAuthButtons (Component)
Description
The OAuthButtons component is a versatile authentication button system designed to handle OAuth providers with consistent styling and behavior. It supports multiple providers including GitHub, Apple, X (Twitter), Google, and a default option with customizable variants and sizes.
Preview

Features
- 🔐 Multiple OAuth Providers (GitHub, Apple, X, Google, Default)
- 🎨 Provider-specific styling with brand colors
- 📱 Responsive design with multiple sizes
- ⚡ Loading states with spinner animations
- 🌙 Dark mode support with automatic theme switching
- ♿ Accessibility with proper ARIA labels
- 🎯 Customizable text and click handlers
Usage
Import
vue
<template>
<OAuthButtons
provider="github"
variant="primary"
size="md"
@click="handleAuth"
/>
</template>1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Props
| Prop | Type | Default | Description |
|---|---|---|---|
provider | 'github' | 'apple' | 'x' | 'google' | 'default' | 'default' | OAuth provider type |
variant | 'primary' | 'secondary' | 'outline' | 'primary' | Button style variant |
size | 'sm' | 'md' | 'lg' | 'md' | Button size |
disabled | boolean | false | Disable button interaction |
loading | boolean | false | Show loading spinner |
text | string | '' | Custom button text |
onClick | () => void | undefined | Click handler function |
Examples
Basic Usage
vue
<template>
<OAuthButtons provider="github" />
</template>1
2
3
2
3
Multiple Providers
vue
<template>
<div class="space-y-3">
<OAuthButtons
provider="github"
@click="handleGitHubAuth"
/>
<OAuthButtons
provider="google"
@click="handleGoogleAuth"
/>
<OAuthButtons
provider="apple"
@click="handleAppleAuth"
/>
<OAuthButtons
provider="x"
@click="handleXAuth"
/>
</div>
</template>
<script setup>
const handleGitHubAuth = () => {
// GitHub OAuth logic
console.log('GitHub authentication')
}
const handleGoogleAuth = () => {
// Google OAuth logic
console.log('Google authentication')
}
const handleAppleAuth = () => {
// Apple OAuth logic
console.log('Apple authentication')
}
const handleXAuth = () => {
// X (Twitter) OAuth logic
console.log('X authentication')
}
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
With Loading States
vue
<template>
<div class="space-y-3">
<OAuthButtons
provider="github"
:loading="isGitHubLoading"
@click="handleGitHubAuth"
/>
<OAuthButtons
provider="google"
:loading="isGoogleLoading"
@click="handleGoogleAuth"
/>
</div>
</template>
<script setup>
const isGitHubLoading = ref(false)
const isGoogleLoading = ref(false)
const handleGitHubAuth = async () => {
isGitHubLoading.value = true
try {
// GitHub OAuth logic
await authenticateWithGitHub()
} finally {
isGitHubLoading.value = false
}
}
const handleGoogleAuth = async () => {
isGoogleLoading.value = true
try {
// Google OAuth logic
await authenticateWithGoogle()
} finally {
isGoogleLoading.value = false
}
}
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Different Sizes
vue
<template>
<div class="space-y-4">
<div>
<label class="text-sm font-medium text-gray-700 mb-2 block">Small</label>
<OAuthButtons provider="github" size="sm" />
</div>
<div>
<label class="text-sm font-medium text-gray-700 mb-2 block">Medium</label>
<OAuthButtons provider="github" size="md" />
</div>
<div>
<label class="text-sm font-medium text-gray-700 mb-2 block">Large</label>
<OAuthButtons provider="github" size="lg" />
</div>
</div>
</template>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Custom Text
vue
<template>
<OAuthButtons
provider="github"
text="Sign in with GitHub"
@click="handleAuth"
/>
</template>1
2
3
4
5
6
7
2
3
4
5
6
7
Disabled State
vue
<template>
<OAuthButtons
provider="github"
:disabled="true"
text="Authentication Unavailable"
/>
</template>1
2
3
4
5
6
7
2
3
4
5
6
7
Provider Configurations
Each provider has specific styling and branding:
GitHub
- Icon:
logos:github-icon - Default Text: "Continue with GitHub"
- Colors: Dark gray background with white text
Apple
- Icon:
logos:apple - Default Text: "Continue with Apple"
- Colors: Black background with white text
X (Twitter)
- Icon:
logos:x - Default Text: "Continue with X"
- Colors: Black background with white text
Google
- Icon:
logos:google-icon - Default Text: "Continue with Google"
- Colors: White background with gray text and border
Default
- Icon:
lucide:user - Default Text: "Continue"
- Colors: Blue background with white text
Styling and Themes
The component uses Tailwind CSS classes and automatically adapts to light/dark themes:
Base Classes
inline-flex items-center justify-center: Flex layoutfont-medium: Medium font weighttransition-all duration-200: Smooth transitionsfocus:outline-none focus:ring-2 focus:ring-offset-2: Focus statesdisabled:opacity-50 disabled:cursor-not-allowed: Disabled states
Size Classes
- Small:
px-3 py-2 text-sm rounded-md - Medium:
px-4 py-2 text-sm rounded-lg - Large:
px-6 py-3 text-base rounded-lg
Provider Colors
Each provider has specific color schemes that work in both light and dark modes.
Loading States
When loading is true:
- Shows a spinning loader icon (
lucide:loader-2) - Disables button interaction
- Updates ARIA label to "Loading..."
Accessibility
- ARIA Labels: Dynamic labels based on loading state
- Keyboard Navigation: Full keyboard support
- Focus Management: Visible focus rings
- Screen Reader Support: Proper semantic markup
Integration Examples
Supabase Auth Integration
vue
<template>
<div class="space-y-3">
<OAuthButtons
provider="github"
:loading="isLoading"
@click="signInWithGitHub"
/>
<OAuthButtons
provider="google"
:loading="isLoading"
@click="signInWithGoogle"
/>
</div>
</template>
<script setup>
const supabase = useSupabaseClient()
const isLoading = ref(false)
const signInWithGitHub = async () => {
isLoading.value = true
try {
const { error } = await supabase.auth.signInWithOAuth({
provider: 'github',
options: {
redirectTo: `${window.location.origin}/auth/callback`
}
})
if (error) throw error
} catch (error) {
console.error('GitHub auth error:', error)
} finally {
isLoading.value = false
}
}
const signInWithGoogle = async () => {
isLoading.value = true
try {
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`
}
})
if (error) throw error
} catch (error) {
console.error('Google auth error:', error)
} finally {
isLoading.value = false
}
}
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Custom OAuth Flow
vue
<template>
<OAuthButtons
provider="github"
:loading="isLoading"
@click="handleCustomAuth"
/>
</template>
<script setup>
const isLoading = ref(false)
const handleCustomAuth = async () => {
isLoading.value = true
try {
// Custom OAuth implementation
const response = await fetch('/api/auth/github', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
if (response.ok) {
const data = await response.json()
// Handle successful authentication
await navigateTo('/dashboard')
} else {
throw new Error('Authentication failed')
}
} catch (error) {
console.error('Authentication error:', error)
// Show error message to user
} finally {
isLoading.value = false
}
}
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Best Practices
- Always handle loading states to provide user feedback
- Use appropriate provider icons for brand consistency
- Implement proper error handling for OAuth flows
- Test accessibility with keyboard navigation
- Provide fallback options for unsupported providers
- Use consistent sizing across your authentication flow
Troubleshooting
Button doesn't respond to clicks
- Check that the
@clickevent is properly attached - Ensure the button is not disabled or in loading state
- Verify the
onClickprop function is defined
Icons don't display
- Verify that the Iconify library is properly configured
- Check that the icon names are correct
- Ensure the icon library is loaded
Styling issues
- Verify Tailwind CSS is configured
- Check for CSS conflicts with other styles
- Ensure dark mode classes are working
OAuth flow not working
- Verify OAuth provider configuration
- Check redirect URLs are properly set
- Ensure API keys and secrets are configured
Complete Example
vue
<template>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-md w-full space-y-8">
<div>
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-white">
Sign in to your account
</h2>
<p class="mt-2 text-center text-sm text-gray-600 dark:text-gray-400">
Choose your preferred authentication method
</p>
</div>
<div class="space-y-4">
<OAuthButtons
provider="github"
size="lg"
:loading="isGitHubLoading"
@click="signInWithGitHub"
/>
<OAuthButtons
provider="google"
size="lg"
:loading="isGoogleLoading"
@click="signInWithGoogle"
/>
<OAuthButtons
provider="apple"
size="lg"
:loading="isAppleLoading"
@click="signInWithApple"
/>
<OAuthButtons
provider="x"
size="lg"
:loading="isXLoading"
@click="signInWithX"
/>
</div>
<div class="text-center">
<p class="text-sm text-gray-600 dark:text-gray-400">
By continuing, you agree to our Terms of Service and Privacy Policy
</p>
</div>
</div>
</div>
</template>
<script setup>
const supabase = useSupabaseClient()
const isLoading = ref(false)
const isGitHubLoading = ref(false)
const isGoogleLoading = ref(false)
const isAppleLoading = ref(false)
const isXLoading = ref(false)
const signInWithProvider = async (provider: string) => {
const loadingRef = {
github: isGitHubLoading,
google: isGoogleLoading,
apple: isAppleLoading,
x: isXLoading
}[provider]
if (loadingRef) {
loadingRef.value = true
}
try {
const { error } = await supabase.auth.signInWithOAuth({
provider: provider as any,
options: {
redirectTo: `${window.location.origin}/auth/callback`
}
})
if (error) throw error
} catch (error) {
console.error(`${provider} auth error:`, error)
// Show error toast or notification
} finally {
if (loadingRef) {
loadingRef.value = false
}
}
}
const signInWithGitHub = () => signInWithProvider('github')
const signInWithGoogle = () => signInWithProvider('google')
const signInWithApple = () => signInWithProvider('apple')
const signInWithX = () => signInWithProvider('x')
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
This component is part of the ClawPlate suite and is optimized for modern SaaS applications with OAuth authentication.