Skip to content

Commit e787a28

Browse files
committed
chore: add docs & gh actions to automate publish
1 parent 5268f25 commit e787a28

7 files changed

Lines changed: 1497 additions & 1537 deletions

File tree

.github/workflows/publish.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Publish
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths-ignore:
7+
- "package.json"
8+
9+
jobs:
10+
publish:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
token: ${{ secrets.GITHUB_TOKEN }}
19+
20+
- name: Install pnpm
21+
uses: pnpm/action-setup@v4
22+
23+
- name: Set up Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: 24
27+
cache: "pnpm"
28+
29+
- name: Install dependencies
30+
run: pnpm install --frozen-lockfile
31+
32+
- name: Build
33+
run: pnpm run build
34+
35+
- name: Bump version and create tag
36+
id: bump_version
37+
run: |
38+
git config --global user.name "GitHub Actions"
39+
git config --global user.email "actions@github.com"
40+
41+
# Automatically bumps the patch version (e.g., 1.0.0 -> 1.0.1)
42+
# Change 'patch' to 'minor' or 'major' depending on your needs
43+
pnpm version patch --no-git-tag-version
44+
45+
# Read the newly bumped version
46+
NEW_VERSION=$(node -p "require('./package.json').version")
47+
echo "New version: $NEW_VERSION"
48+
49+
# Commit and tag
50+
git add package.json
51+
git commit -m "chore(release): bump version to $NEW_VERSION [skip ci]"
52+
git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION"
53+
54+
# Push changes securely
55+
git push origin HEAD:main --tags
56+
57+
# Export for next steps
58+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
59+
60+
- name: Publish to npm
61+
run: |
62+
# Create npm registry auth configuration dynamically
63+
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > .npmrc
64+
pnpm publish --no-git-checks
65+
env:
66+
NODE_AUTH_TOKEN: ${{ secrets.PNPM_TOKEN }}
67+
68+
- name: Create GitHub Release
69+
uses: softprops/action-gh-release@v2
70+
with:
71+
tag_name: "v${{ steps.bump_version.outputs.version }}"
72+
name: "Release v${{ steps.bump_version.outputs.version }}"
73+
body: |
74+
Automated release of **v${{ steps.bump_version.outputs.version }}** built from commit `${{ github.sha }}`.
75+
draft: false
76+
prerelease: false
77+
env:
78+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
24.18.0

README.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# @mtayyabrawan/formhook
2+
3+
A lightweight React hook for managing forms with built-in Zod validation. Simplify form state, validation, and submission handling in React and Next.js applications.
4+
5+
## Features
6+
7+
- **React Hook API**: `useForm<T>()` provides a simple interface for form state management.
8+
- **Zod Validation**: Integrates seamlessly with Zod schemas for type-safe validation.
9+
- **Type Safety**: Full TypeScript support with inferred types for form values and errors.
10+
- **Flexible API**: Supports field-level access via `formField`, error handling via `formErrors`, and easy reset functionality.
11+
- **Next.js Ready**: Works out of the box with Server Components and App Router.
12+
13+
## Installation
14+
15+
```bash
16+
npm install @mtayyabrawan/formhook
17+
# or
18+
yarn add @mtayyabrawan/formhook
19+
```
20+
21+
## Peer Dependencies
22+
23+
- React (^18 or ^19)
24+
- Zod (^3.25.0 or ^4)
25+
26+
Make sure these are installed in your project.
27+
28+
## Basic Usage
29+
30+
```tsx
31+
import { useForm } from "@mtayyabrawan/formhook";
32+
import { z } from "zod";
33+
34+
const schema = z.object({
35+
name: z.string().min(3, "Name is required"),
36+
email: z.string().email("Invalid email"),
37+
age: z.number().int().min(18, "Must be 18+"),
38+
});
39+
40+
export default function UserForm() {
41+
const { reset, formField, formErrors, handleSubmit } = useForm({
42+
initialData: { name: "", email: "", age: 0 },
43+
validationSchema: schema,
44+
});
45+
46+
const onSubmit = (data: { name: string; email: string; age: number }) => {
47+
console.log("Form submitted:", data);
48+
};
49+
50+
return (
51+
<form onSubmit={handleSubmit(onSubmit)}>
52+
<div>
53+
<label>
54+
Name
55+
<input type="text" {...formField("name")} />
56+
</label>
57+
{formErrors.name && (
58+
<p className="text-red-500">{formErrors.name}</p>
59+
)}
60+
</div>
61+
62+
<div>
63+
<label>
64+
Email
65+
<input type="email" {...formField("email")} />
66+
</label>
67+
{formErrors.email && (
68+
<p className="text-red-500">{formErrors.email}</p>
69+
)}
70+
</div>
71+
72+
<div>
73+
<label>
74+
Age
75+
<input type="number" {...formField("age")} />
76+
</label>
77+
{formErrors.age && (
78+
<p className="text-red-500">{formErrors.age}</p>
79+
)}
80+
</div>
81+
82+
<button type="submit">Submit</button>
83+
<button type="button" onClick={reset}>
84+
Reset
85+
</button>
86+
</form>
87+
);
88+
}
89+
```
90+
91+
### Key API Elements
92+
93+
- **`useForm<T>(props: UseFormProps<T>)`**: Returns an object with form state and methods.
94+
- **`formField<K extends keyof T>(fieldName: K)`**: Accessor for a specific field, returns `value`, `name`, and `onChange`.
95+
- **`formErrors: FormErrors<T>`**: Object containing validation errors keyed by field name.
96+
- **`reset()`**: Resets form to initial data and clears errors.
97+
- **`handleSubmit(callback: (data: T) => void)`**: Wraps submit handler to trigger validation and provide `event.preventDefault()`.
98+
99+
### Using with Next.js App Router
100+
101+
The hook works seamlessly with Next.js 13+ App Router. Since it's a client-side hook, wrap your form component in a `"use client"` directive:
102+
103+
```tsx
104+
"use client";
105+
106+
import { useForm } from "@mtayyabrawan/formhook";
107+
import { z } from "zod";
108+
109+
const schema = z.object({
110+
username: z.string().min(3),
111+
password: z.string().min(8),
112+
});
113+
114+
export default function LoginForm() {
115+
const { formField, formErrors, handleSubmit } = useForm({
116+
initialData: { username: "", password: "" },
117+
validationSchema: schema,
118+
});
119+
120+
const onSubmit = (data: { username: string; password: string }) => {
121+
// Handle login logic
122+
};
123+
124+
return (
125+
<form onSubmit={handleSubmit(onSubmit)}>
126+
<input {...formField("username")} />
127+
{formErrors.username && <p>{formErrors.username}</p>}
128+
129+
<input type="password" {...formField("password")} />
130+
{formErrors.password && <p>{formErrors.password}</p>}
131+
132+
<button type="submit">Login</button>
133+
</form>
134+
);
135+
}
136+
```
137+
138+
## FAQ
139+
140+
**Q: Is the hook compatible with React 19?**
141+
A: Yes, it supports React 18 and 19.
142+
143+
**Q: How do I reset the form programmatically?**
144+
A: Call the `reset` method returned by `useForm`.
145+
146+
## License
147+
148+
MIT © [Muhammad Tayyab](https://linkedin.com/in/mtayyabrawan)
149+
150+
---
151+
152+
_Happy form building!_ 🚀

0 commit comments

Comments
 (0)