AuthForm

FormGitHub
A customizable Form to create login, register or password reset forms.

Usage

Built on top of the Form component, the AuthForm component can be used in your pages or wrapped in a PageCard.

Login
Enter your credentials to access your account.
<script setup lang="ts">
import type { AuthFormField, FormSubmitEvent } from 'pohon-ui';
import * as z from 'zod';

const toast = useToast();

const fields: Array<AuthFormField> = [{
  name: 'email',
  type: 'email',
  label: 'Email',
  placeholder: 'Enter your email',
  required: true,
}, {
  name: 'password',
  label: 'Password',
  type: 'password',
  placeholder: 'Enter your password',
  required: true,
}, {
  name: 'remember',
  label: 'Remember me',
  type: 'checkbox',
}];

const providers = [{
  label: 'Google',
  icon: 'i-simple-icons-google',
  onClick: () => {
    toast.add({ title: 'Google', description: 'Login with Google' });
  },
}, {
  label: 'GitHub',
  icon: 'i-simple-icons-github',
  onClick: () => {
    toast.add({ title: 'GitHub', description: 'Login with GitHub' });
  },
}];

const schema = z.object({
  email: z.email('Invalid email'),
  password: z.string('Password is required').min(8, 'Must be at least 8 characters'),
});

type Schema = z.output<typeof schema>;

function onSubmit(payload: FormSubmitEvent<Schema>) {
  console.log('Submitted', payload);
}
</script>

<template>
  <div class="p-4 flex flex-col gap-4 items-center justify-center">
    <PPageCard class="max-w-md w-full">
      <PAuthForm
        :schema="schema"
        title="Login"
        description="Enter your credentials to access your account."
        icon="i-lucide-user"
        :fields="fields"
        :providers="providers"
        @submit="onSubmit"
      />
    </PPageCard>
  </div>
</template>

Fields

The Form will construct itself based on the fields prop and the state will be handled internally.

Use the fields prop as an array of objects with the following properties:

  • name: string
  • type: 'checkbox' | 'select' | 'otp' | 'InputHTMLAttributes['type']'

Each field must include a type property, which determines the input component and any additional props applied: checkbox fields use Checkbox props, select fields use SelectMenu props, otp fields use PinInput props, and all other types use Input props.

You can also pass any property from the FormField component to each field.

You will be logged in for 30 days.

<script setup lang="ts">
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'email',
    label: 'Email',
    placeholder: 'Enter your email',
    required: true
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password',
    placeholder: 'Enter your password',
    required: true
  },
  {
    name: 'country',
    type: 'select',
    label: 'Country',
    placeholder: 'Select country',
    items: [
      {
        label: 'United States',
        value: 'us'
      },
      {
        label: 'France',
        value: 'fr'
      },
      {
        label: 'United Kingdom',
        value: 'uk'
      },
      {
        label: 'Australia',
        value: 'au'
      }
    ]
  },
  {
    name: 'otp',
    type: 'otp',
    label: 'OTP',
    length: 6,
    placeholder: '○'
  },
  {
    name: 'remember',
    type: 'checkbox',
    label: 'Remember me',
    description: 'You will be logged in for 30 days.'
  }
])
</script>

<template>
  <PAuthForm :fields="fields" class="max-w-sm" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'email',
    label: 'Email',
    placeholder: 'Enter your email',
    required: true
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password',
    placeholder: 'Enter your password',
    required: true
  },
  {
    name: 'country',
    type: 'select',
    label: 'Country',
    placeholder: 'Select country',
    items: [
      {
        label: 'United States',
        value: 'us'
      },
      {
        label: 'France',
        value: 'fr'
      },
      {
        label: 'United Kingdom',
        value: 'uk'
      },
      {
        label: 'Australia',
        value: 'au'
      }
    ]
  },
  {
    name: 'otp',
    type: 'otp',
    label: 'OTP',
    length: 6,
    placeholder: '○'
  },
  {
    name: 'remember',
    type: 'checkbox',
    label: 'Remember me',
    description: 'You will be logged in for 30 days.'
  }
])
</script>

<template>
  <PAuthForm :fields="fields" class="max-w-sm" />
</template>

Title

Use the title prop to set the title of the Form.

Login
<script setup lang="ts">
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm title="Login" :fields="fields" class="max-w-md" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm title="Login" :fields="fields" class="max-w-md" />
</template>
The title is rendered as a div by default. Use the as prop with an object to render it as a heading, for example :as="{ title: 'h3' }". You can also set the root element this way: :as="{ root: 'section', title: 'h3' }".

Description

Use the description prop to set the description of the Form.

Login
Enter your credentials to access your account.
<script setup lang="ts">
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    :fields="fields"
    class="max-w-md"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    :fields="fields"
    class="max-w-md"
  />
</template>

Icon

Use the icon prop to set the icon of the Form.

Login
Enter your credentials to access your account.
<script setup lang="ts">
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :fields="fields"
    class="max-w-md"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :fields="fields"
    class="max-w-md"
  />
</template>

Providers

Use the providers prop to add providers to the form.

You can pass any property from the Button component such as variant, color, to, etc.

Login
Enter your credentials to access your account.
<script setup lang="ts">
import type { ButtonProps, AuthFormField } from 'pohon-ui'

const providers = ref<ButtonProps[]>([
  {
    label: 'Google',
    icon: 'i-simple-icons-google',
    color: 'neutral',
    variant: 'subtle'
  },
  {
    label: 'GitHub',
    icon: 'i-simple-icons-github',
    color: 'neutral',
    variant: 'subtle'
  }
])
const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :providers="providers"
    :fields="fields"
    class="max-w-md"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ButtonProps, AuthFormField } from 'pohon-ui'

const providers = ref<ButtonProps[]>([
  {
    label: 'Google',
    icon: 'i-simple-icons-google',
    color: 'neutral',
    variant: 'subtle'
  },
  {
    label: 'GitHub',
    icon: 'i-simple-icons-github',
    color: 'neutral',
    variant: 'subtle'
  }
])
const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :providers="providers"
    :fields="fields"
    class="max-w-md"
  />
</template>

Separator

Use the separator prop to customize the Separator between the providers and the fields. Defaults to or.

Login
Enter your credentials to access your account.
<script setup lang="ts">
import type { ButtonProps, AuthFormField } from 'pohon-ui'

const providers = ref<ButtonProps[]>([
  {
    label: 'Google',
    icon: 'i-simple-icons-google',
    color: 'neutral',
    variant: 'subtle'
  },
  {
    label: 'GitHub',
    icon: 'i-simple-icons-github',
    color: 'neutral',
    variant: 'subtle'
  }
])
const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :providers="providers"
    :fields="fields"
    separator="Providers"
    class="max-w-md"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ButtonProps, AuthFormField } from 'pohon-ui'

const providers = ref<ButtonProps[]>([
  {
    label: 'Google',
    icon: 'i-simple-icons-google',
    color: 'neutral',
    variant: 'subtle'
  },
  {
    label: 'GitHub',
    icon: 'i-simple-icons-github',
    color: 'neutral',
    variant: 'subtle'
  }
])
const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :providers="providers"
    :fields="fields"
    separator="Providers"
    class="max-w-md"
  />
</template>

You can pass any property from the Separator component to customize it.

Login
Enter your credentials to access your account.
<script setup lang="ts">
import type { ButtonProps, AuthFormField } from 'pohon-ui'

const providers = ref<ButtonProps[]>([
  {
    label: 'Google',
    icon: 'i-simple-icons-google',
    color: 'neutral',
    variant: 'subtle'
  },
  {
    label: 'GitHub',
    icon: 'i-simple-icons-github',
    color: 'neutral',
    variant: 'subtle'
  }
])
const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :providers="providers"
    :fields="fields"
    :separator="{
      icon: 'i-lucide-user'
    }"
    class="max-w-md"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ButtonProps, AuthFormField } from 'pohon-ui'

const providers = ref<ButtonProps[]>([
  {
    label: 'Google',
    icon: 'i-simple-icons-google',
    color: 'neutral',
    variant: 'subtle'
  },
  {
    label: 'GitHub',
    icon: 'i-simple-icons-github',
    color: 'neutral',
    variant: 'subtle'
  }
])
const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :providers="providers"
    :fields="fields"
    :separator="{
      icon: 'i-lucide-user'
    }"
    class="max-w-md"
  />
</template>

Submit

Use the submit prop to change the submit button of the Form.

You can pass any property from the Button component such as variant, color, to, etc.

Login
Enter your credentials to access your account.
<script setup lang="ts">
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :fields="fields"
    :submit="{
      label: 'Submit',
      color: 'error',
      variant: 'subtle'
    }"
    class="max-w-md"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AuthFormField } from 'pohon-ui'

const fields = ref<AuthFormField[]>([
  {
    name: 'email',
    type: 'text',
    label: 'Email'
  },
  {
    name: 'password',
    type: 'password',
    label: 'Password'
  }
])
</script>

<template>
  <PAuthForm
    title="Login"
    description="Enter your credentials to access your account."
    icon="i-lucide-user"
    :fields="fields"
    :submit="{
      label: 'Submit',
      color: 'error',
      variant: 'subtle'
    }"
    class="max-w-md"
  />
</template>

Examples

Within a page

You can wrap the AuthForm component with the PageCard component to display it within a login.vue page for example.

Welcome back!
Don't have an account? Sign up .
Error signing in
By signing in, you agree to our Terms of Service .
<script setup lang="ts">
import type { AuthFormField, FormSubmitEvent } from 'pohon-ui';
import * as z from 'zod';

const toast = useToast();

const fields: Array<AuthFormField> = [{
  name: 'email',
  type: 'email',
  label: 'Email',
  placeholder: 'Enter your email',
  required: true,
}, {
  name: 'password',
  label: 'Password',
  type: 'password',
  placeholder: 'Enter your password',
  required: true,
}, {
  name: 'remember',
  label: 'Remember me',
  type: 'checkbox',
}];

const providers = [{
  label: 'Google',
  icon: 'i-simple-icons-google',
  onClick: () => {
    toast.add({ title: 'Google', description: 'Login with Google' });
  },
}, {
  label: 'GitHub',
  icon: 'i-simple-icons-github',
  onClick: () => {
    toast.add({ title: 'GitHub', description: 'Login with GitHub' });
  },
}];

const schema = z.object({
  email: z.email('Invalid email'),
  password: z.string('Password is required').min(8, 'Must be at least 8 characters'),
});

type Schema = z.output<typeof schema>;

function onSubmit(payload: FormSubmitEvent<Schema>) {
  console.log('Submitted', payload);
}
</script>

<template>
  <div class="p-4 flex flex-col gap-4 items-center justify-center">
    <PPageCard class="max-w-md w-full">
      <PAuthForm
        :schema="schema"
        :fields="fields"
        :providers="providers"
        title="Welcome back!"
        icon="i-lucide-lock"
        @submit="onSubmit"
      >
        <template #description>
          Don't have an account? <PLink
            to="#"
            class="color-primary font-medium"
          >
            Sign up
          </PLink>.
        </template>
        <template #password-hint>
          <PLink
            to="#"
            class="color-primary font-medium"
            tabindex="-1"
          >
            Forgot password?
          </PLink>
        </template>
        <template #validation>
          <PAlert
            color="error"
            icon="i-lucide-info"
            title="Error signing in"
          />
        </template>
        <template #footer>
          By signing in, you agree to our <PLink
            to="#"
            class="color-primary font-medium"
          >
            Terms of Service
          </PLink>.
        </template>
      </PAuthForm>
    </PPageCard>
  </div>
</template>

API

Props

Prop Default Type
This component also supports all native <form> HTML attributes.

Slots

Slot Type

Emits

Event Type

Expose

You can access the typed component instance (exposing formRef and state) using useTemplateRef. For example, in a separate form (e.g. a "reset" form) you can do:

<script setup lang="ts">
const authForm = useTemplateRef('authForm');
</script>

<template>
  <PAuthForm ref="authForm" />
</template>

This gives you access to the following (exposed) properties:

NameType
formRefRef<HTMLFormElement | null>
stateReactive<FormStateType>

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    authForm: {
      slots: {
        root: 'w-full space-y-6',
        header: 'flex flex-col text-center',
        leading: 'mb-2',
        leadingIcon: 'size-8 shrink-0 inline-block',
        title: 'text-xl text-pretty font-600 color-text-highlighted',
        description: 'mt-1 text-base text-pretty color-text-muted',
        body: 'gap-y-6 flex flex-col',
        providers: 'space-y-3',
        checkbox: '',
        select: 'w-full',
        password: 'w-full',
        otp: 'w-full',
        input: 'w-full',
        separator: '',
        form: 'space-y-5',
        footer: 'text-sm text-center color-text-muted mt-2'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        authForm: {
          slots: {
            root: 'w-full space-y-6',
            header: 'flex flex-col text-center',
            leading: 'mb-2',
            leadingIcon: 'size-8 shrink-0 inline-block',
            title: 'text-xl text-pretty font-600 color-text-highlighted',
            description: 'mt-1 text-base text-pretty color-text-muted',
            body: 'gap-y-6 flex flex-col',
            providers: 'space-y-3',
            checkbox: '',
            select: 'w-full',
            password: 'w-full',
            otp: 'w-full',
            input: 'w-full',
            separator: '',
            form: 'space-y-5',
            footer: 'text-sm text-center color-text-muted mt-2'
          }
        }
      }
    })
  ]
})

Changelog

No recent changes