Skip to content

Commit 2f7fddd

Browse files
committed
Initial commit
0 parents  commit 2f7fddd

17 files changed

+7597
-0
lines changed

.gitignore

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
21+
# debug
22+
npm-debug.log*
23+
yarn-debug.log*
24+
yarn-error.log*
25+
26+
# local env files
27+
.env.local
28+
.env.development.local
29+
.env.test.local
30+
.env.production.local

README.md

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Next.js with react-bootstrap and form validation
2+
3+
[Starting from this example](https://git.1-hub.cnvercel/next.js/tree/canary/examples/with-react-bootstrap), this project was my experiment to see how form validation could be done easily and effectively with [React Bootstrap](https://react-bootstrap.netlify.app/) as the UI framework.
4+
5+
I tried two scenarios where my goal was to minimize complexity, tediousness, and stay as true to React/HTML5 as possible.
6+
7+
[React Hook Form](https://react-hook-form.com/) is minimally intrusive, easy to use, and is fairly flexible. With React Bootstrap it only worked with the "ref" approach using [register](https://react-hook-form.com/api#register). The [Controller](https://react-hook-form.com/api#Controller) approach didn't work for me. The downside to the "ref" approach is that I'm guessing it is using an uncontrolled strategy, which is [not recommended by React](https://reactjs.org/docs/uncontrolled-components.html).
8+
9+
The other approach, which I ended up preferring, was to use [React Bootstrap's support for native HTML5 validation](https://react-bootstrap.netlify.app/components/forms/#forms-validation-native). It was easy to activate and not surprisingly integrates more easily with Bootstrap's `Form.Control.Feedback` component. What really sold me on this approach is that HTML5 already provides a `validationMessage`, [described here](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#Validating_forms_using_JavaScript), that conveys a helpful description of why the field is invalid.
10+
11+
Here is the view of the required field after the initial form submission:
12+
13+
![](docs/empty-field.png)
14+
15+
As typing characters, but still below the `minLength={3}` declaration:
16+
17+
![](docs/too-short.png)
18+
19+
Finally, when typing at least another character the rendering flips to a positive indication and feedback removal:
20+
21+
![](docs/valid.png)
22+
23+
With that knowledge I wrapped up the potential repetition in a little helper component that paired a `Form.Control` with a `Form.Control.Feedback`. That helper component made use of the `validationMessage`, the `name` on the input, and the [`invalid` event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event) fired by inputs during validation.
24+
25+
Using that helper component each input is about 10 lines, which is quite acceptable, for example:
26+
27+
```jsx
28+
<ValidatedFormControl onChange={handleChange}
29+
value={values.yourName}
30+
name="yourName"
31+
type="text" placeholder="Enter your name"
32+
required
33+
minLength={3}
34+
/>
35+
```
36+
37+
That `ValidatedFormControl` doesn't require much code itself:
38+
```jsx
39+
function ValidatedFormControl({
40+
value,
41+
onChange,
42+
...props
43+
}) {
44+
const [message, setMessage] = useState();
45+
const { controlId } = useContext(FormContext);
46+
47+
return (
48+
<>
49+
<Form.Control {...props}
50+
value={value}
51+
onChange={event => {
52+
onChange(event.target.value, event.target.name || controlId);
53+
event.target.checkValidity();
54+
}}
55+
onInvalid={event => setMessage(
56+
event.target.validationMessage)}
57+
/>
58+
<Form.Control.Feedback type="invalid">
59+
{message}
60+
</Form.Control.Feedback>
61+
</>
62+
)
63+
}
64+
```
65+
66+
## Deploy your own
67+
68+
Deploy this project using [Vercel](https://vercel.com):
69+
70+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://git.1-hub.cnitzg/nextjs-bootstrap-validation)

components/header.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {Nav, Navbar} from "react-bootstrap";
2+
import Link from "next/link";
3+
4+
export default function Header() {
5+
return (
6+
<Navbar>
7+
<Nav>
8+
<Link href="/" passHref>
9+
<Nav.Link>Home</Nav.Link>
10+
</Link>
11+
<Link href="/hook-form" passHref>
12+
<Nav.Link>Hook Form</Nav.Link>
13+
</Link>
14+
<Link href="/bs-native-html5" passHref>
15+
<Nav.Link>Bootstrap Native HTML5</Nav.Link>
16+
</Link>
17+
</Nav>
18+
</Navbar>
19+
)
20+
}

components/linkNewTab.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default function LinkNewTab({href, children}) {
2+
return (
3+
<a href={href} target="_blank">{children}</a>
4+
);
5+
}

components/submittedValues.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {Card, Table} from "react-bootstrap";
2+
3+
export default function SubmittedValues({values}) {
4+
return (
5+
<Card className="mt-3">
6+
<Card.Header>Submitted</Card.Header>
7+
<Card.Body>
8+
<Table borderless size="sm">
9+
<tbody>
10+
{Object.entries(values).map(([k, v]) =>
11+
<tr key={k}>
12+
<th>{k}</th>
13+
<td>{v}</td>
14+
</tr>
15+
)}
16+
</tbody>
17+
</Table>
18+
</Card.Body>
19+
</Card>
20+
)
21+
}

docs/empty-field.png

3.93 KB
Loading

docs/too-short.png

5.26 KB
Loading

docs/valid.png

2.14 KB
Loading

0 commit comments

Comments
 (0)