npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

webspark-ui

v1.0.1

Published

Reusable React and Next.js components with Tailwind CSS. Optional React Hook Form and Zod support.

Downloads

3

Readme

webspark-ui

A collection of reusable React and Next.js components built with Tailwind CSS. Works with or without React Hook Form and Zod validation. Perfect for building modern, type-safe forms and UI interfaces.

Installation

npm install webspark-ui

Required Dependencies

The following dependencies are required:

npm install react react-dom

Optional Dependencies

The following dependencies are optional and only needed if you want to use specific features:

  • React Hook Form (react-hook-form): For form validation with InputField component
  • Zod (zod): For schema validation (used with React Hook Form)
# Install only if you need form validation
npm install react-hook-form zod

Components

Button

A flexible button component with multiple variants and sizes. Works seamlessly with both React and Next.js applications.

Props

| Prop | Type | Default | Description | | -------------- | ------------------------------------------------------ | -------------- | ------------------------------------- | | variant | 'primary' \| 'second' \| 'tertiary' \| 'blackButton' | 'primary' | Button style variant | | size | 'sm' \| 'md' \| 'lg' | 'md' | Button size | | fullWidth | boolean | false | Make button full width | | fullWidthSM | boolean | false | Full width on mobile, auto on desktop | | type | 'button' \| 'submit' \| 'reset' | 'button' | Button type | | disabled | boolean | false | Disable the button | | borderRadius | string | 'rounded-xl' | Custom border radius class | | leftIcon | ReactNode | - | Icon to display on the left | | rightIcon | ReactNode | - | Icon to display on the right | | className | string | '' | Additional CSS classes | | onClick | function | - | Click handler |

Usage Examples

import { Button } from "webastic-ui";

// Basic button
<Button variant="primary">Click Me</Button>

// Different variants
<Button variant="primary">Primary Button</Button>
<Button variant="second">Secondary Button</Button>
<Button variant="tertiary">Tertiary Button</Button>
<Button variant="blackButton">Black Button</Button>


<Button size="sm">Small Button</Button>
<Button size="md">Medium Button</Button>
<Button size="lg">Large Button</Button>

// Full width button
<Button fullWidth>Full Width Button</Button>

// Button with icons
<Button
  leftIcon={<Icon />}
  rightIcon={<ArrowIcon />}
>
  Button with Icons
</Button>

// Submit button
<Button type="submit" variant="primary">
  Submit Form
</Button>

// Disabled button
<Button disabled variant="primary">
  Disabled Button
</Button>

// Custom styling
<Button
  borderRadius="rounded-full"
  className="shadow-lg"
>
  Custom Styled Button
</Button>

InputField

A comprehensive input field component that works with or without React Hook Form. Supports validation errors and various input types. Compatible with both React and Next.js.

Props

| Prop | Type | Default | Description | | ------------- | ------------------ | ------------ | ---------------------------------------------------- | | name | string | required | Field name (used for form registration) | | label | string | - | Label text for the input | | placeholder | string | - | Placeholder text | | type | string | 'text' | Input type (text, email, password, etc.) | | value | string | - | Controlled input value | | onChange | function | - | Change handler | | required | boolean | false | Mark field as required | | disabled | boolean | false | Disable the input | | register | function | - | React Hook Form register function (optional) | | errors | object \| string | - | Validation errors object or error message (optional) | | area | boolean | false | Render as textarea instead of input | | className | string | 'relative' | Additional CSS classes | | widthClass | string | - | Custom width class | | instruction | string | - | Helper text below input | | min | any | - | Minimum value (for number inputs) | | svgprop | ReactNode | - | SVG icon component to display inside input | | iconsrc | string | - | Image icon source URL | | iconclass | string | '' | CSS classes for the icon image | | iconName | string | - | Icon name (for accessibility) | | areaClass | string | '' | Additional CSS classes for textarea element |

Usage Examples

import { InputField } from "webastic-ui";
import { useForm } from "react-hook-form";

// Basic input field (works without React Hook Form)
<InputField
  name="username"
  label="Username"
  placeholder="Enter your username"
/>

// With controlled value (without React Hook Form)
<InputField
  name="email"
  label="Email"
  placeholder="Enter your email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
/>

// With React Hook Form (optional)
function MyForm() {
  const { register, formState: { errors } } = useForm();

  return (
    <form>
      <InputField
        name="email"
        label="Email Address"
        placeholder="Enter your email"
        type="email"
        register={register("email", {
          required: "Email is required",
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: "Invalid email address"
          }
        })}
        errors={errors}
        required
      />

      <InputField
        name="password"
        label="Password"
        placeholder="Enter your password"
        type="password"
        register={register("password", {
          required: "Password is required",
          minLength: {
            value: 8,
            message: "Password must be at least 8 characters"
          }
        })}
        errors={errors}
        required
      />
    </form>
  );
}

// Textarea
<InputField
  name="message"
  label="Message"
  placeholder="Enter your message"
  area={true}
  required
/>

// Disabled input
<InputField
  name="readonly"
  label="Read Only Field"
  value="Cannot be edited"
  disabled
/>

// With custom instruction
<InputField
  name="phone"
  label="Phone Number"
  placeholder="Enter phone number"
  instruction="Include country code (e.g., +1)"
/>

// Number input
<InputField
  name="age"
  label="Age"
  type="number"
  min={18}
  placeholder="Enter your age"
/>

// With custom width
<InputField
  name="search"
  label="Search"
  placeholder="Search..."
  widthClass="w-64"
/>

// With SVG icon
<InputField
  name="email"
  label="Email"
  placeholder="Enter email"
  svgprop={<EmailIcon />}
/>

// With image icon
<InputField
  name="username"
  label="Username"
  placeholder="Enter username"
  iconsrc="/icons/user.svg"
  iconclass="w-5 h-5"
/>

// Textarea with custom area class
<InputField
  name="message"
  label="Message"
  placeholder="Enter your message"
  area={true}
  areaClass="custom-textarea-class"
  required
/>

DropDown

A flexible dropdown component that supports both single and multi-select options. Works seamlessly with React Hook Form (using register) or without React Hook Form (using value and onChange). Includes error handling and is compatible with both React and Next.js.

Props

| Prop | Type | Default | Description | | ------------- | ------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------- | | label | string | - | Label text for the dropdown | | options | { value: T; label: string }[] | required | Array of options (T extends string | number) | | value | T \| T[] | - | Selected value(s) - single value or array for multi-select (required when not using register) | | onChange | function | - | Change handler function (required when not using register) | | isMulti | boolean | false | Enable multi-select mode | | placeholder | string | 'Select an option' | Placeholder text when no option is selected | | name | string | - | Field name (used for form registration and error handling) | | required | boolean | false | Mark field as required | | className | string | 'relative' | Additional CSS classes | | disabled | boolean | false | Disable the dropdown | | errors | object \| string | - | Validation errors object or error message (optional) | | svgprop | ReactNode | - | SVG icon component to display on the left side | | register | function | - | React Hook Form register function (optional, use with name prop) | | instruction | string | - | Helper text below dropdown |

Note: You can use the component in two ways:

  • With React Hook Form: Provide register and name props (no need for value and onChange)
  • Without React Hook Form: Provide value and onChange props

Usage Examples

import { DropDown } from "webastic-ui";
import { useForm } from "react-hook-form";

// ===== WITHOUT REACT HOOK FORM =====

// Basic single-select dropdown
const [selectedValue, setSelectedValue] = useState<string>("");

<DropDown
  label="Country"
  options={[
    { value: "us", label: "United States" },
    { value: "uk", label: "United Kingdom" },
    { value: "ca", label: "Canada" },
  ]}
  value={selectedValue}
  onChange={setSelectedValue}
  placeholder="Select a country"
/>

// Multi-select dropdown
const [selectedValues, setSelectedValues] = useState<string[]>([]);

<DropDown
  label="Select Languages"
  options={[
    { value: "en", label: "English" },
    { value: "es", label: "Spanish" },
    { value: "fr", label: "French" },
    { value: "de", label: "German" },
  ]}
  value={selectedValues}
  onChange={setSelectedValues}
  isMulti={true}
  placeholder="Select languages"
/>

// ===== WITH REACT HOOK FORM =====

// Single-select with React Hook Form
function MyForm() {
  const { register, formState: { errors } } = useForm();

  return (
    <form>
      <DropDown
        name="country"
        label="Country"
        options={[
          { value: "us", label: "United States" },
          { value: "uk", label: "United Kingdom" },
          { value: "ca", label: "Canada" },
        ]}
        register={register("country", { required: "Country is required" })}
        errors={errors}
        required
        placeholder="Select a country"
      />
    </form>
  );
}

// Multi-select with React Hook Form
function MultiSelectForm() {
  const { register, formState: { errors } } = useForm();

  return (
    <form>
      <DropDown
        name="languages"
        label="Select Languages"
        options={[
          { value: "en", label: "English" },
          { value: "es", label: "Spanish" },
          { value: "fr", label: "French" },
        ]}
        register={register("languages", { required: "Please select at least one language" })}
        errors={errors}
        isMulti={true}
        required
      />
    </form>
  );
}

// With error handling (without React Hook Form)
const [errors, setErrors] = useState<Record<string, string>>({});

<DropDown
  name="category"
  label="Category"
  options={[
    { value: "tech", label: "Technology" },
    { value: "design", label: "Design" },
    { value: "marketing", label: "Marketing" },
  ]}
  value={category}
  onChange={setCategory}
  errors={errors}
  required
/>

// With SVG icon
<DropDown
  label="Select Option"
  options={options}
  value={value}
  onChange={setValue}
  svgprop={<LocationIcon />}
/>

// With instruction text
<DropDown
  label="Priority"
  options={[
    { value: "low", label: "Low" },
    { value: "medium", label: "Medium" },
    { value: "high", label: "High" },
  ]}
  value={priority}
  onChange={setPriority}
  instruction="Select the priority level for this task"
/>

// Disabled dropdown
<DropDown
  label="Status"
  options={options}
  value={status}
  onChange={setStatus}
  disabled
/>

// Number-based options
const [age, setAge] = useState<number | number[]>([]);

<DropDown
  label="Age Range"
  options={[
    { value: 18, label: "18-25" },
    { value: 26, label: "26-35" },
    { value: 36, label: "36-45" },
  ]}
  value={age}
  onChange={setAge}
  isMulti={true}
/>

CheckBox

A customizable checkbox component with clean styling. Uses the primary color for borders and checkmarks. Compatible with both React and Next.js.

Props

| Prop | Type | Default | Description | | ---------- | ---------- | ------------ | ---------------------------------------------------- | | label | string | required | Label text for the checkbox | | checked | boolean | required | Checked state | | onChange | function | required | Change handler function (checked: boolean) => void | | disabled | boolean | false | Disable the checkbox |

Usage Examples

import { CheckBox } from "webastic-ui";

// Basic checkbox
const [isChecked, setIsChecked] = useState(false);

<CheckBox
  label="I agree to the terms and conditions"
  checked={isChecked}
  onChange={setIsChecked}
/>

// Checkbox in a form
function ConsentForm() {
  const [agreed, setAgreed] = useState(false);

  return (
    <form>
      <CheckBox
        label="I accept the privacy policy"
        checked={agreed}
        onChange={setAgreed}
      />

      <Button
        type="submit"
        variant="primary"
        disabled={!agreed}
      >
        Continue
      </Button>
    </form>
  );
}

// Disabled checkbox
<CheckBox
  label="This option is disabled"
  checked={false}
  onChange={() => {}}
  disabled
/>

// Multiple checkboxes
const [preferences, setPreferences] = useState({
  newsletter: false,
  notifications: false,
  updates: false,
});

<CheckBox
  label="Subscribe to newsletter"
  checked={preferences.newsletter}
  onChange={(checked) =>
    setPreferences({ ...preferences, newsletter: checked })
  }
/>

<CheckBox
  label="Enable notifications"
  checked={preferences.notifications}
  onChange={(checked) =>
    setPreferences({ ...preferences, notifications: checked })
  }
/>

Complete Form Examples

Example 1: Without React Hook Form (Simple Form with Error Messages)

import { useState } from "react";
import { Button, InputField } from "webastic-ui";

function ContactForm() {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    message: "",
  });

  const [errors, setErrors] = useState<Record<string, string>>({});

  const validateEmail = (email: string) => {
    const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
    return emailRegex.test(email);
  };

  const handleChange = (
    e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
  ) => {
    const { name, value } = e.target;
    setFormData({
      ...formData,
      [name]: value,
    });

    // Clear error when user starts typing
    if (errors[name]) {
      setErrors({
        ...errors,
        [name]: "",
      });
    }
  };

  const validateForm = () => {
    const newErrors: Record<string, string> = {};

    if (!formData.name.trim()) {
      newErrors.name = "Name is required";
    } else if (formData.name.trim().length < 2) {
      newErrors.name = "Name must be at least 2 characters";
    }

    if (!formData.email.trim()) {
      newErrors.email = "Email is required";
    } else if (!validateEmail(formData.email)) {
      newErrors.email = "Please enter a valid email address";
    }

    if (!formData.message.trim()) {
      newErrors.message = "Message is required";
    } else if (formData.message.trim().length < 10) {
      newErrors.message = "Message must be at least 10 characters";
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();

    if (validateForm()) {
      console.log(formData);
      // Handle form submission
      alert("Form submitted successfully!");
    }
  };

  return (
    <form onSubmit={handleSubmit} className="max-w-md mx-auto space-y-4">
      <InputField
        name="name"
        label="Full Name"
        placeholder="Enter your name"
        value={formData.name}
        onChange={handleChange}
        errors={errors}
        required
      />

      <InputField
        name="email"
        label="Email"
        placeholder="Enter your email"
        type="email"
        value={formData.email}
        onChange={handleChange}
        errors={errors}
        required
      />

      <InputField
        name="message"
        label="Message"
        placeholder="Enter your message"
        area={true}
        value={formData.message}
        onChange={handleChange}
        errors={errors}
        required
      />

      <Button type="submit" variant="primary" fullWidth>
        Submit
      </Button>
    </form>
  );
}

Example 2: With React Hook Form and Zod (Advanced Form)

import { useForm } from "react-hook-form";
import { Button, InputField } from "webastic-ui";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

// Define validation schema
const formSchema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters"),
  email: z.string().email("Invalid email address"),
  message: z.string().min(10, "Message must be at least 10 characters"),
});

type FormData = z.infer<typeof formSchema>;

function ContactForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>({
    resolver: zodResolver(formSchema),
  });

  const onSubmit = (data: FormData) => {
    console.log(data);
    // Handle form submission
  };

  return (
    <form
      onSubmit={handleSubmit(onSubmit)}
      className="max-w-md mx-auto space-y-4"
    >
      <InputField
        name="name"
        label="Full Name"
        placeholder="Enter your name"
        register={register("name")}
        errors={errors}
        required
      />

      <InputField
        name="email"
        label="Email"
        placeholder="Enter your email"
        type="email"
        register={register("email")}
        errors={errors}
        required
      />

      <InputField
        name="message"
        label="Message"
        placeholder="Enter your message"
        area={true}
        register={register("message")}
        errors={errors}
        required
      />

      <Button type="submit" variant="primary" fullWidth>
        Submit
      </Button>
    </form>
  );
}

Styling

This package uses Tailwind CSS for styling. Make sure you have Tailwind CSS configured in your project:

  1. Install Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
  1. Configure your tailwind.config.js:
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
    "./node_modules/webastic-ui/**/*.{js,jsx,ts,tsx}",
  ],
  // ... rest of your config
};

Important Note

Primary Color Configuration: Some components (like Button, InputField, CheckBox) use the primary Tailwind class (e.g., border-primary, focus:border-primary, focus:ring-primary/20). Make sure you have the primary color defined in your Tailwind configuration. If not defined, you can add it to your tailwind.config.js:

module.exports = {
  theme: {
    extend: {
      colors: {
        primary: "#your-primary-color", // e.g., '#3b82f6' for blue
      },
    },
  },
  // ... rest of your config
};

Alternatively, you can modify the component's className to use standard Tailwind colors like border-blue-500 or any other color class that matches your design system.

React and Next.js Compatibility

This package works seamlessly with both:

  • React applications (Create React App, Vite, etc.)
  • Next.js applications (Pages Router and App Router)

All components are framework-agnostic and use standard React patterns.

TypeScript Support

This package is written in TypeScript and includes type definitions. All components are fully typed for better developer experience.

License

MIT