Input Components

Beautiful, accessible input components built with TailwindCSS. Perfect for forms, search bars, and user interactions. Copy, paste, and customize for React, Vue, or HTML.

1. Minimal Input

A clean, minimal input with subtle border and focus states. Perfect for simple forms and clean interfaces.

<div>
  <label for="minimal-input" class="block text-sm font-medium text-gray-700 mb-2">Email Address</label>
  <input type="email" id="minimal-input" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="you@example.com">
</div>

<div>
  <label for="minimal-password" class="block text-sm font-medium text-gray-700 mb-2">Password</label>
  <input type="password" id="minimal-password" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="••••••••">
</div>
<template>
  <div class="space-y-6">
    <div>
      <label for="email" class="block text-sm font-medium text-gray-700 mb-2">Email Address</label>
      <input 
        type="email" 
        id="email" 
        v-model="email"
        class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" 
        placeholder="you@example.com"
      >
    </div>
    
    <div>
      <label for="password" class="block text-sm font-medium text-gray-700 mb-2">Password</label>
      <input 
        type="password" 
        id="password" 
        v-model="password"
        class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" 
        placeholder="••••••••"
      >
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const email = ref('')
const password = ref('')
</script>
import React, { useState } from 'react'

export default function MinimalInput() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  return (
    <div className="space-y-6">
      <div>
        <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
          Email Address
        </label>
        <input
          type="email"
          id="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
          placeholder="you@example.com"
        />
      </div>
      
      <div>
        <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-2">
          Password
        </label>
        <input
          type="password"
          id="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
          placeholder="••••••••"
        />
      </div>
    </div>
  )
}

2. Input with Heroicons

Enhanced inputs with Heroicons for better visual hierarchy and user experience. Includes search and user icons.

<div class="relative">
  <label for="search-input" class="block text-sm font-medium text-gray-700 mb-2">Search</label>
  <div class="relative">
    <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
      <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
      </svg>
    </div>
    <input type="text" id="search-input" class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="Search...">
  </div>
</div>

<div class="relative">
  <label for="user-input" class="block text-sm font-medium text-gray-700 mb-2">Full Name</label>
  <div class="relative">
    <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
      <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
      </svg>
    </div>
    <input type="text" id="user-input" class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="John Doe">
  </div>
</div>
<template>
  <div class="space-y-6">
    <div class="relative">
      <label for="search" class="block text-sm font-medium text-gray-700 mb-2">Search</label>
      <div class="relative">
        <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
          <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
          </svg>
        </div>
        <input 
          type="text" 
          id="search" 
          v-model="searchQuery"
          class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" 
          placeholder="Search..."
        >
      </div>
    </div>
    
    <div class="relative">
      <label for="fullName" class="block text-sm font-medium text-gray-700 mb-2">Full Name</label>
      <div class="relative">
        <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
          <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
          </svg>
        </div>
        <input 
          type="text" 
          id="fullName" 
          v-model="fullName"
          class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" 
          placeholder="John Doe"
        >
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const searchQuery = ref('')
const fullName = ref('')
</script>
import React, { useState } from 'react'

export default function InputWithIcons() {
  const [searchQuery, setSearchQuery] = useState('')
  const [fullName, setFullName] = useState('')

  return (
    <div className="space-y-6">
      <div className="relative">
        <label htmlFor="search" className="block text-sm font-medium text-gray-700 mb-2">
          Search
        </label>
        <div className="relative">
          <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
            <svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
            </svg>
          </div>
          <input
            type="text"
            id="search"
            value={searchQuery}
            onChange={(e) => setSearchQuery(e.target.value)}
            className="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
            placeholder="Search..."
          />
        </div>
      </div>
      
      <div className="relative">
        <label htmlFor="fullName" className="block text-sm font-medium text-gray-700 mb-2">
          Full Name
        </label>
        <div className="relative">
          <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
            <svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
            </svg>
          </div>
          <input
            type="text"
            id="fullName"
            value={fullName}
            onChange={(e) => setFullName(e.target.value)}
            className="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
            placeholder="John Doe"
          />
        </div>
      </div>
    </div>
  )
}

3. Neobrutalist Text Input

Bold, modern input with thick borders, shadows, and vibrant focus states.

<!-- Basic Neobrutalist Input -->
<div>
  <label class="block text-sm font-black uppercase tracking-wider mb-2" for="email">Email Address</label>
  <input 
    type="email" 
    id="email"
    placeholder="your@email.com"
    class="w-full px-4 py-3 border-4 border-black bg-white text-black placeholder-gray-500 font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:shadow-[6px_6px_0px_0px_rgba(139,92,246,1)] focus:border-violet focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]"
  >
</div>

<!-- Colored Variant -->
<div>
  <label class="block text-sm font-black uppercase tracking-wider mb-2" for="name">Full Name</label>
  <input 
    type="text" 
    id="name"
    placeholder="John Doe"
    class="w-full px-4 py-3 border-4 border-black bg-[#ffde59] text-black placeholder-gray-700 font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:shadow-[6px_6px_0px_0px_rgba(255,92,92,1)] focus:border-[#ff5c5c] focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]"
  >
</div>
<template>
  <div>
    <label :for="inputId" class="block text-sm font-black uppercase tracking-wider mb-2">
      {{ label }}
    </label>
    <input 
      :type="type"
      :id="inputId"
      :placeholder="placeholder"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
      :class="inputClasses"
    >
  </div>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  label: String,
  type: { type: String, default: 'text' },
  placeholder: String,
  modelValue: String,
  variant: { type: String, default: 'default' },
  inputId: String
})

const emit = defineEmits(['update:modelValue'])

const inputClasses = computed(() => {
  const baseClasses = 'w-full px-4 py-3 border-4 border-black font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]'
  
  const variants = {
    default: 'bg-white text-black placeholder-gray-500 focus:shadow-[6px_6px_0px_0px_rgba(139,92,246,1)] focus:border-violet',
    yellow: 'bg-[#ffde59] text-black placeholder-gray-700 focus:shadow-[6px_6px_0px_0px_rgba(255,92,92,1)] focus:border-[#ff5c5c]'
  }
  
  return `${baseClasses} ${variants[props.variant] || variants.default}`
})
</script>
import React from 'react'

const NeobrutalistInput = ({ 
  label, 
  type = 'text', 
  placeholder, 
  value, 
  onChange, 
  variant = 'default',
  id,
  ...props 
}) => {
  const getInputClasses = () => {
    const baseClasses = 'w-full px-4 py-3 border-4 border-black font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]'
    
    const variants = {
      default: 'bg-white text-black placeholder-gray-500 focus:shadow-[6px_6px_0px_0px_rgba(139,92,246,1)] focus:border-violet',
      yellow: 'bg-[#ffde59] text-black placeholder-gray-700 focus:shadow-[6px_6px_0px_0px_rgba(255,92,92,1)] focus:border-[#ff5c5c]'
    }
    
    return `${baseClasses} ${variants[variant] || variants.default}`
  }

  return (
    <div>
      {label && (
        <label htmlFor={id} className="block text-sm font-black uppercase tracking-wider mb-2">
          {label}
        </label>
      )}
      <input
        type={type}
        id={id}
        placeholder={placeholder}
        value={value}
        onChange={onChange}
        className={getInputClasses()}
        {...props}
      />
    </div>
  )
}

export default NeobrutalistInput

4. Floating Label Input

Elegant floating label design with smooth animations and modern styling.

<!-- Text Input with Floating Label -->
<div class="relative">
  <input type="email" id="floating-email" class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer" placeholder=" " />
  <label for="floating-email" class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1">Email address</label>
</div>

<!-- Password Input with Floating Label -->
<div class="relative">
  <input type="password" id="floating-password" class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer" placeholder=" " />
  <label for="floating-password" class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1">Password</label>
</div>

<!-- Textarea with Floating Label -->
<div class="relative">
  <textarea id="floating-message" rows="4" class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer resize-none" placeholder=" "></textarea>
  <label for="floating-message" class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-6 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1">Your message</label>
</div>
<template>
  <div class="relative">
    <input 
      :type="type"
      :id="inputId"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
      class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer" 
      placeholder=" " 
    />
    <label 
      :for="inputId" 
      :class="labelClasses"
    >
      {{ label }}
    </label>
  </div>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  label: { type: String, required: true },
  type: { type: String, default: 'text' },
  modelValue: String,
  inputId: { type: String, required: true },
  isTextarea: { type: Boolean, default: false }
})

const emit = defineEmits(['update:modelValue'])

const labelClasses = computed(() => {
  const baseClasses = 'absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1'
  
  return props.isTextarea 
    ? `${baseClasses} peer-placeholder-shown:top-6`
    : `${baseClasses} peer-placeholder-shown:top-1/2`
})
</script>
import React from 'react'

const FloatingLabelInput = ({ 
  label, 
  type = 'text', 
  value, 
  onChange, 
  id,
  isTextarea = false,
  rows = 4,
  ...props 
}) => {
  const getLabelClasses = () => {
    const baseClasses = 'absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1'
    
    return isTextarea 
      ? `${baseClasses} peer-placeholder-shown:top-6`
      : `${baseClasses} peer-placeholder-shown:top-1/2`
  }

  const inputClasses = "block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer"

  return (
    <div className="relative">
      {isTextarea ? (
        <textarea
          id={id}
          value={value}
          onChange={onChange}
          rows={rows}
          className={`${inputClasses} resize-none`}
          placeholder=" "
          {...props}
        />
      ) : (
        <input
          type={type}
          id={id}
          value={value}
          onChange={onChange}
          className={inputClasses}
          placeholder=" "
          {...props}
        />
      )}
      <label htmlFor={id} className={getLabelClasses()}>
        {label}
      </label>
    </div>
  )
}

export default FloatingLabelInput

We use cookies to improve your experience and analytics. You can accept all cookies or reject non-essential ones.

Learn More