Vue templateFree

Auth Pages

Sign in, sign up and forgot-password screens in Vue 3 with a split brand panel, inline validation and a password visibility toggle. Tailwind CSS, dark mode.

by Vueplayv1.0.0Updated 2 hours ago

Install
Full screen ↗

About this template

The three authentication screens almost every product needs, sharing one split layout: a brand panel with a customer quote on large screens, and a focused form column that works on its own on phones. Switch between screens with the links in each form, the small switcher at the top, or the URL hash (#signin, #signup, #forgot).

Fields validate on blur and on submit with clear messages tied to inputs through aria-describedby. The password field has an accessible show/hide toggle and, on sign up, a strength meter. Submitting shows a loading state and a success message; replace fakeRequest in validation.ts with your API calls.

The single sign-on buttons are generic (SSO and passkey) so you can swap in the providers you support using their official brand assets. TextField and PasswordField are small reusable components you can lift into the rest of your app.

  • Sign in, sign up and forgot password with a sent-confirmation state
  • Inline validation, strength meter and show/hide password
  • Split layout with a drawn brand panel, single column on mobile
  • Hash-based view state and focus management between screens
  • Light and dark mode

Code

19 files in the repo
<script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue'
import BrandPanel from './components/BrandPanel.vue'
import SignInForm from './components/SignInForm.vue'
import SignUpForm from './components/SignUpForm.vue'
import ForgotPasswordForm from './components/ForgotPasswordForm.vue'
import type { AuthView } from './validation'

const views: { id: AuthView; label: string }[] = [
  { id: 'signin', label: 'Sign in' },
  { id: 'signup', label: 'Sign up' },
  { id: 'forgot', label: 'Forgot password' },
]

// The view is mirrored to the URL hash (#signin, #signup, #forgot) so each screen can be linked to.
function fromHash(): AuthView {
  const h = window.location.hash.slice(1)
  return views.some((v) => v.id === h) ? (h as AuthView) : 'signin'
}

const view = ref<AuthView>(fromHash())
const notice = ref('')
const panel = useTemplateRef<HTMLElement>('panel')

function navigate(next: AuthView) {
  notice.value = ''
  view.value = next
}

watch(view, async (v) => {
  if (window.location.hash.slice(1) !== v) history.replaceState(null, '', `#${v}`)
  await nextTick()
  // Move focus to the new screen's heading for screen reader and keyboard users.
  const heading = panel.value?.querySelector<HTMLElement>('h1')
  if (heading) {
    heading.tabIndex = -1
    heading.focus()
  }
})

const onHashChange = () => { view.value = fromHash() }
onMounted(() => window.addEventListener('hashchange', onHashChange))
onBeforeUnmount(() => window.removeEventListener('hashchange', onHashChange))
</script>

<template>
  <div class="grid min-h-screen bg-white lg:grid-cols-[minmax(0,5fr)_minmax(0,6fr)] dark:bg-gray-950">
    <BrandPanel />

    <main class="flex flex-col px-5 py-8 sm:px-10 lg:px-16">
      <!-- Mobile brand + screen switcher (handy while previewing the template) -->
      <div class="flex items-center justify-between gap-4">
        <span class="flex items-center gap-2 font-semibold text-gray-900 lg:invisible dark:text-white">
          <span class="grid size-8 place-items-center rounded-lg bg-emerald-600 text-white" aria-hidden="true">
            <svg viewBox="0 0 24 24" class="size-4" fill="currentColor"><path d="M12 2l5 7h-3l4 6h-3.5l3.5 5H6l3.5-5H6l4-6H7l5-7z" /></svg>
          </span>
          Northpine
        </span>
        <nav aria-label="Auth screens" class="flex rounded-lg bg-gray-100 p-0.5 text-xs font-medium dark:bg-white/5">
          <button
            v-for="v in views"
            :key="v.id"
            type="button"
            class="rounded-md px-2.5 py-1.5 transition focus-visible:outline-2 focus-visible:outline-emerald-500"
            :class="view === v.id ? 'bg-white text-gray-900 shadow-sm dark:bg-white/10 dark:text-white' : 'text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-white'"
            :aria-current="view === v.id ? 'page' : undefined"
            @click="navigate(v.id)"
          >{{ v.id === 'forgot' ? 'Reset' : v.label }}</button>
        </nav>
      </div>

      <div ref="panel" class="mx-auto flex w-full max-w-sm flex-1 flex-col justify-center py-12 [&_h1]:outline-none">
        <div v-if="notice" role="status" class="mb-6 flex gap-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3.5 text-sm text-emerald-800 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-200">
          <svg viewBox="0 0 20 20" class="size-5 shrink-0" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 10.5l3 3 7-7" /></svg>
          {{ notice }}
        </div>
        <Transition mode="out-in" enter-active-class="transition duration-200" enter-from-class="translate-y-1 opacity-0" leave-active-class="transition duration-100" leave-to-class="opacity-0">
          <SignInForm v-if="view === 'signin'" @navigate="navigate" @success="notice = $event" />
          <SignUpForm v-else-if="view === 'signup'" @navigate="navigate" @success="notice = $event" />
          <ForgotPasswordForm v-else @navigate="navigate" />
        </Transition>
      </div>

      <p class="text-center text-xs text-gray-500 dark:text-gray-400">
        Protected by rate limiting and optional two-factor authentication. Northpine is a fictional product.
      </p>
    </main>
  </div>
</template>
All auth pages →
Preview of Settings PageFree

Template · Dashboards

Settings Page

Account settings template for Vue 3 and Tailwind CSS with section nav, profile and account forms, notification toggles, billing and a floating save bar.

by Vueplay
Preview of 404 Not Found PageFree

Template · Error pages

404 Not Found Page

Friendly Vue 3 404 page with a drawn compass illustration, live search suggestions and helpful links, plus a compact 500 error variant. Tailwind CSS.

by Vueplay
Preview of Admin Dashboard$29

Template · Dashboards

Admin Dashboard

A complete Vue 3 admin dashboard template with a collapsible sidebar, hand-drawn SVG charts, a sortable customers table and settings. Tailwind CSS, dark mode.

by Vueplay
Preview of Analytics DashboardFree

Template · Dashboards

Analytics Dashboard

Web analytics dashboard template for Vue 3 and Tailwind CSS: SVG area chart, metric tabs, top pages, sources, a devices donut and a date range picker.

by Vueplay

Pro feature

Upgrade to Pro

Upgrade to unlock more of Vueplay. Cancel any time.

  • Custom domains with HTTPS
  • Private projects
  • No "Made with Vueplay" badge
  • GitHub sync

$120/year

Compare plansBilling settings