@vuehookform/core 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 vuehookform
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,57 +1,44 @@
1
- # Vue Hook Form 📝
1
+ # Vue Hook Form
2
2
 
3
- > A TypeScript-first form library for Vue 3, inspired by React Hook Form
3
+ A TypeScript-first form library for Vue 3, inspired by React Hook Form.
4
4
 
5
- **Vue Hook Form** makes form handling in Vue.js feel like a superpower. Built with performance, developer experience, and type safety as top priorities.
5
+ ## Features
6
6
 
7
- ## Features
7
+ - **TypeScript First** - Perfect type inference with zero manual typing
8
+ - **Zero Config** - Works out of the box with sensible defaults
9
+ - **Performant** - Minimal re-renders using uncontrolled inputs
10
+ - **Zod Native** - First-class Zod integration for validation
11
+ - **Tiny Bundle** - < 5kb gzipped, tree-shakable
12
+ - **UI Agnostic** - Works with any UI library or custom components
8
13
 
9
- - **🎯 TypeScript First** - Perfect type inference with zero manual typing
10
- - **⚡ Zero Config** - Works out of the box with sensible defaults
11
- - **🚀 Performant** - Minimal re-renders using uncontrolled inputs
12
- - **🔥 Zod Native** - First-class Zod integration for validation
13
- - **📦 Tiny Bundle** - Tree-shakable and dependency-free (< 10kb gzipped)
14
- - **🎨 UI Agnostic** - Works with any UI library or custom components
15
- - **🔌 Composable-First** - Built for Vue 3's Composition API
16
-
17
- ## 🚀 Quick Start
18
-
19
- ### Installation
14
+ ## Quick Start
20
15
 
21
16
  ```bash
22
17
  npm install @vuehookform/core zod
23
- # or
24
- bun add @vuehookform/core zod
25
18
  ```
26
19
 
27
- ### Basic Usage
28
-
29
20
  ```vue
30
21
  <script setup lang="ts">
31
22
  import { useForm } from '@vuehookform/core'
32
23
  import { z } from 'zod'
33
24
 
34
- // 1. Define your schema
35
25
  const schema = z.object({
36
26
  email: z.email('Invalid email'),
37
27
  password: z.string().min(8, 'At least 8 characters'),
38
28
  })
39
29
 
40
- // 2. Initialize form
41
30
  const { register, handleSubmit, formState } = useForm({
42
31
  schema,
43
32
  mode: 'onBlur',
44
33
  })
45
34
 
46
- // 3. Handle submission
47
35
  const onSubmit = (data) => {
48
- console.log(data) // Fully typed! { email: string, password: string }
36
+ console.log(data) // Fully typed: { email: string, password: string }
49
37
  }
50
38
  </script>
51
39
 
52
40
  <template>
53
41
  <form @submit="handleSubmit(onSubmit)">
54
- <!-- Bind inputs with v-bind -->
55
42
  <input v-bind="register('email')" type="email" />
56
43
  <span v-if="formState.errors.email">{{ formState.errors.email }}</span>
57
44
 
@@ -63,33 +50,10 @@ const onSubmit = (data) => {
63
50
  </template>
64
51
  ```
65
52
 
66
- That's it! Three steps and you have a fully validated, type-safe form. 🎉
67
-
68
- ## 📚 Examples
69
-
70
- This repo includes working examples showcasing all features:
71
-
72
- ### Run Examples
73
-
74
- ```bash
75
- # Install dependencies
76
- npm install
77
-
78
- # Start dev server
79
- npm run dev
80
- ```
81
-
82
- Then open http://localhost:5173 to see:
83
-
84
- 1. **Basic Form** - Simple validation, error handling, form state
85
- 2. **Dynamic Arrays** - Add/remove nested form sections dynamically
86
-
87
- ## 💡 Key Concepts
53
+ ## Key Concepts
88
54
 
89
55
  ### Schema as Source of Truth
90
56
 
91
- Define your form structure and validation in one place:
92
-
93
57
  ```typescript
94
58
  const userSchema = z.object({
95
59
  name: z.string().min(2),
@@ -97,20 +61,15 @@ const userSchema = z.object({
97
61
  age: z.number().min(18),
98
62
  })
99
63
 
100
- // Types are automatically inferred
101
64
  type UserForm = z.infer<typeof userSchema>
102
65
  // { name: string; email: string; age: number }
103
66
  ```
104
67
 
105
- ### Zero-Boilerplate Dynamic Arrays
106
-
107
- Managing dynamic form sections is trivial:
68
+ ### Dynamic Arrays
108
69
 
109
70
  ```vue
110
71
  <script setup>
111
72
  const { register, fields } = useForm({ schema })
112
-
113
- // Get field array manager
114
73
  const addresses = fields('addresses')
115
74
  </script>
116
75
 
@@ -119,194 +78,60 @@ const addresses = fields('addresses')
119
78
  <input v-bind="register(`addresses.${field.index}.street`)" />
120
79
  <button @click="field.remove()">Remove</button>
121
80
  </div>
122
-
123
81
  <button @click="addresses.append({ street: '', city: '' })">Add Address</button>
124
82
  </template>
125
83
  ```
126
84
 
127
85
  ### Validation Modes
128
86
 
129
- Control when validation runs:
130
-
131
87
  ```typescript
132
88
  useForm({
133
89
  schema,
134
- mode: 'onSubmit', // Only validate on submit (default)
135
- // mode: 'onBlur', // Validate when field loses focus
136
- // mode: 'onChange', // Validate on every keystroke
137
- // mode: 'onTouched', // Validate after field is touched
90
+ mode: 'onSubmit', // Only validate on submit (default)
91
+ // mode: 'onBlur', // Validate when field loses focus
92
+ // mode: 'onChange', // Validate on every keystroke
93
+ // mode: 'onTouched', // Validate after field is touched
138
94
  })
139
95
  ```
140
96
 
141
- ## 🎨 API Reference
97
+ ## API Reference
142
98
 
143
99
  ### `useForm(options)`
144
100
 
145
- Main composable for form management.
146
-
147
- **Options:**
148
-
149
101
  ```typescript
150
- {
151
- schema: ZodSchema // Zod schema for validation
152
- defaultValues?: Partial<T> // Initial form values
153
- mode?: ValidationMode // When to validate
154
- }
155
- ```
156
-
157
- **Returns:**
158
-
159
- ```typescript
160
- {
161
- register: (name, options?) => RegisterReturn
162
- handleSubmit: (onValid, onInvalid?) => (e) => Promise<void>
163
- formState: ComputedRef<FormState>
164
- fields: (name) => FieldArray
165
- setValue: (name, value) => void
166
- getValue: (name) => any
167
- reset: (values?) => void
168
- watch: (name?) => ComputedRef<any>
169
- validate: (name?) => Promise<boolean>
170
- }
171
- ```
172
-
173
- ### `register(name, options?)`
174
-
175
- Register an input field for validation and state management.
176
-
177
- ```vue
178
- <input v-bind="register('email')" />
179
- <input v-bind="register('email', { controlled: true })" />
180
- ```
181
-
182
- ### `handleSubmit(onValid, onInvalid?)`
183
-
184
- Create submit handler with validation.
185
-
186
- ```typescript
187
- const onSubmit = handleSubmit(
188
- (data) => {
189
- // Called with validated data
190
- console.log(data)
191
- },
192
- (errors) => {
193
- // Optional: called when validation fails
194
- console.log(errors)
195
- },
196
- )
102
+ const {
103
+ register, // Register input field
104
+ handleSubmit, // Create submit handler with validation
105
+ formState, // Reactive form state (errors, isSubmitting, etc.)
106
+ fields, // Manage dynamic field arrays
107
+ setValue, // Programmatically set field value
108
+ getValue, // Get current field value
109
+ reset, // Reset form to default values
110
+ watch, // Watch field value changes
111
+ validate, // Manually trigger validation
112
+ } = useForm({
113
+ schema, // Zod schema for validation
114
+ defaultValues: {}, // Initial form values
115
+ mode: 'onSubmit', // When to validate
116
+ })
197
117
  ```
198
118
 
199
119
  ### `fields(name)`
200
120
 
201
- Manage dynamic field arrays.
202
-
203
121
  ```typescript
204
122
  const addresses = fields('addresses')
205
123
 
206
- addresses.append({ street: '', city: '' }) // Add item
207
- addresses.remove(0) // Remove by index
208
- addresses.insert(1, value) // Insert at index
209
- addresses.swap(0, 1) // Swap two items
210
- addresses.move(0, 2) // Move item
124
+ addresses.append({ street: '', city: '' })
125
+ addresses.remove(0)
126
+ addresses.insert(1, value)
127
+ addresses.swap(0, 1)
128
+ addresses.move(0, 2)
211
129
  ```
212
130
 
213
- ## 🏗️ Project Structure
214
-
215
- ```
216
- src/
217
- ├── lib/ # Library source code
218
- │ ├── index.ts # Public exports
219
- │ ├── useForm.ts # Main composable
220
- │ ├── types.ts # TypeScript definitions
221
- │ └── utils/ # Helper functions
222
- ├── examples/ # Demo applications
223
- │ ├── BasicForm.vue
224
- │ └── DynamicArrays.vue
225
- └── App.vue # Example showcase
226
- ```
227
-
228
- ## 🎯 Why Vue Hook Form?
229
-
230
- ### vs VeeValidate
231
-
232
- - **Less boilerplate** - One composable manages entire form
233
- - **Better performance** - Uncontrolled inputs by default
234
- - **Simpler API** - Form-level vs field-level management
235
-
236
- ### vs FormKit
237
-
238
- - **Lighter** - < 10kb vs 50kb+
239
- - **Less opinionated** - No UI components required
240
- - **Native Zod** - First-class integration, not an adapter
241
-
242
- ### vs Vuelidate
243
-
244
- - **Type-safe** - Perfect TypeScript inference
245
- - **Built-in arrays** - Dynamic fields work out of the box
246
- - **Modern** - Built for Composition API
247
-
248
- ## 🔧 Development
249
-
250
- ### Setup
251
-
252
- ```bash
253
- npm install
254
- ```
255
-
256
- ### Run Dev Server
257
-
258
- ```bash
259
- npm run dev
260
- ```
261
-
262
- ### Type Check
263
-
264
- ```bash
265
- npm run type-check
266
- ```
267
-
268
- ### Lint
269
-
270
- ```bash
271
- npm run lint
272
- ```
273
-
274
- ### Build
275
-
276
- ```bash
277
- npm run build
278
- ```
279
-
280
- ## 📖 Documentation
281
-
282
- For detailed implementation notes, architecture decisions, and future roadmap, see [CORE_CONCEPTS.md](./CORE_CONCEPTS.md).
283
-
284
- ## 🤝 Contributing
285
-
286
- Contributions welcome! This is a proof-of-concept library demonstrating:
287
-
288
- - Form-level state management
289
- - Zod-first validation
290
- - TypeScript-first design
291
- - Performance-optimized architecture
292
-
293
- Feel free to:
294
-
295
- - Report bugs
296
- - Suggest features
297
- - Submit PRs
298
- - Ask questions
299
-
300
- ## 📝 License
301
-
302
- MIT - Build something awesome!
303
-
304
- ## 🙏 Inspiration
131
+ ## Contributing
305
132
 
306
- - [React Hook Form](https://react-hook-form.com/) - API design inspiration
307
- - [Zod](https://zod.dev/) - Schema validation
308
- - [VeeValidate](https://vee-validate.logaretm.com/) - Vue form validation patterns
133
+ Contributions welcome! Feel free to report bugs, suggest features, or submit PRs.
309
134
 
310
- ---
135
+ ## License
311
136
 
312
- **Made with ❤️ for the Vue community**
137
+ MIT
package/dist/context.d.ts CHANGED
@@ -35,4 +35,3 @@ export declare function provideForm<TSchema extends ZodType>(methods: UseFormRet
35
35
  * @throws Error if used outside of a FormProvider context
36
36
  */
37
37
  export declare function useFormContext<TSchema extends ZodType>(): UseFormReturn<TSchema>;
38
- //# sourceMappingURL=context.d.ts.map
@@ -8,6 +8,14 @@ export interface FieldArrayState {
8
8
  items: Ref<FieldArrayItem[]>;
9
9
  values: unknown[];
10
10
  }
11
+ /**
12
+ * Cached event handlers for a field to prevent recreation on every render
13
+ */
14
+ export interface FieldHandlers {
15
+ onInput: (e: Event) => Promise<void>;
16
+ onBlur: (e: Event) => Promise<void>;
17
+ refCallback: (el: unknown) => void;
18
+ }
11
19
  /**
12
20
  * Shared form context containing all reactive state
13
21
  * This is passed to sub-modules via dependency injection
@@ -16,14 +24,15 @@ export interface FormContext<FormValues> {
16
24
  formData: Record<string, unknown>;
17
25
  defaultValues: Record<string, unknown>;
18
26
  errors: ShallowRef<FieldErrors<FormValues>>;
19
- touchedFields: Ref<Record<string, boolean>>;
20
- dirtyFields: Ref<Record<string, boolean>>;
27
+ touchedFields: ShallowRef<Record<string, boolean>>;
28
+ dirtyFields: ShallowRef<Record<string, boolean>>;
21
29
  isSubmitting: Ref<boolean>;
22
30
  isLoading: Ref<boolean>;
23
31
  submitCount: Ref<number>;
24
32
  fieldRefs: Map<string, Ref<HTMLInputElement | null>>;
25
33
  fieldOptions: Map<string, RegisterOptions>;
26
34
  fieldArrays: Map<string, FieldArrayState>;
35
+ fieldHandlers: Map<string, FieldHandlers>;
27
36
  debounceTimers: Map<string, ReturnType<typeof setTimeout>>;
28
37
  validationRequestIds: Map<string, number>;
29
38
  options: UseFormOptions<ZodType>;
@@ -32,4 +41,3 @@ export interface FormContext<FormValues> {
32
41
  * Create a new form context with all reactive state initialized
33
42
  */
34
43
  export declare function createFormContext<TSchema extends ZodType>(options: UseFormOptions<TSchema>): FormContext<InferSchema<TSchema>>;
35
- //# sourceMappingURL=formContext.d.ts.map
@@ -6,4 +6,3 @@ import { FieldArray, Path } from '../types';
6
6
  export declare function createFieldArrayManager<FormValues>(ctx: FormContext<FormValues>, validate: (fieldPath?: string) => Promise<boolean>): {
7
7
  fields: <TPath extends Path<FormValues>>(name: TPath) => FieldArray;
8
8
  };
9
- //# sourceMappingURL=useFieldArray.d.ts.map
@@ -7,4 +7,3 @@ export declare function createFieldRegistration<FormValues>(ctx: FormContext<For
7
7
  register: <TPath extends Path<FormValues>>(name: TPath, registerOptions?: RegisterOptions) => RegisterReturn;
8
8
  unregister: <TPath extends Path<FormValues>>(name: TPath) => void;
9
9
  };
10
- //# sourceMappingURL=useFieldRegistration.d.ts.map
@@ -5,4 +5,3 @@ import { FormContext } from './formContext';
5
5
  export declare function createValidation<FormValues>(ctx: FormContext<FormValues>): {
6
6
  validate: (fieldPath?: string) => Promise<boolean>;
7
7
  };
8
- //# sourceMappingURL=useValidation.d.ts.map
package/dist/index.d.ts CHANGED
@@ -20,4 +20,3 @@ export { useWatch, type UseWatchOptions } from './useWatch';
20
20
  export { useController, type UseControllerOptions, type UseControllerReturn, type ControllerFieldProps } from './useController';
21
21
  export { useFormState, type UseFormStateOptions, type FormStateKey } from './useFormState';
22
22
  export type { UseFormOptions, UseFormReturn, RegisterOptions, RegisterReturn, FormState, FieldState, FieldErrors, FieldError, FieldErrorValue, FieldArray, FieldArrayItem, ValidationMode, InferSchema, Path, PathValue, ErrorOption, SetFocusOptions, ResetOptions, AsyncDefaultValues, } from './types';
23
- //# sourceMappingURL=index.d.ts.map
package/dist/types.d.ts CHANGED
@@ -287,4 +287,3 @@ export interface UseFormReturn<TSchema extends ZodType> {
287
287
  */
288
288
  setFocus: <TPath extends Path<InferSchema<TSchema>>>(name: TPath, options?: SetFocusOptions) => void;
289
289
  }
290
- //# sourceMappingURL=types.d.ts.map
@@ -61,4 +61,3 @@ export interface UseControllerReturn<TValue> {
61
61
  * ```
62
62
  */
63
63
  export declare function useController<TSchema extends ZodType, TPath extends Path<InferSchema<TSchema>>>(options: UseControllerOptions<TSchema, TPath>): UseControllerReturn<PathValue<InferSchema<TSchema>, TPath>>;
64
- //# sourceMappingURL=useController.d.ts.map
package/dist/useForm.d.ts CHANGED
@@ -18,4 +18,3 @@ import { UseFormOptions, UseFormReturn } from './types';
18
18
  * ```
19
19
  */
20
20
  export declare function useForm<TSchema extends ZodType>(options: UseFormOptions<TSchema>): UseFormReturn<TSchema>;
21
- //# sourceMappingURL=useForm.d.ts.map
@@ -37,4 +37,3 @@ export interface UseFormStateOptions<TSchema extends ZodType> {
37
37
  * ```
38
38
  */
39
39
  export declare function useFormState<TSchema extends ZodType>(options?: UseFormStateOptions<TSchema>): ComputedRef<Partial<FormState<InferSchema<TSchema>>>>;
40
- //# sourceMappingURL=useFormState.d.ts.map
@@ -38,4 +38,3 @@ export interface UseWatchOptions<TSchema extends ZodType, TPath extends Path<Inf
38
38
  * ```
39
39
  */
40
40
  export declare function useWatch<TSchema extends ZodType, TPath extends Path<InferSchema<TSchema>> = Path<InferSchema<TSchema>>>(options?: UseWatchOptions<TSchema, TPath>): ComputedRef<unknown>;
41
- //# sourceMappingURL=useWatch.d.ts.map
@@ -2,7 +2,7 @@
2
2
  * Get value from object using dot notation path
3
3
  * @example get({ user: { name: 'John' } }, 'user.name') => 'John'
4
4
  */
5
- export declare function get(obj: Record<string, unknown>, path: string): unknown;
5
+ export declare function get(obj: unknown, path: string): unknown;
6
6
  /**
7
7
  * Set value in object using dot notation path
8
8
  * @example set({}, 'user.name', 'John') => { user: { name: 'John' } }
@@ -34,4 +34,3 @@ export declare function getParentPath(path: string): string | undefined;
34
34
  * @example getFieldName('user.addresses.0.street') => 'street'
35
35
  */
36
36
  export declare function getFieldName(path: string): string;
37
- //# sourceMappingURL=paths.d.ts.map