diff --git a/src/app/input-form.tsx b/src/app/input-form.tsx
index bed9b76..ef2729e 100644
--- a/src/app/input-form.tsx
+++ b/src/app/input-form.tsx
@@ -10,6 +10,7 @@ import {
 import styles from "./input-form.module.css";
 import "./input-form.css";
 import {State} from "@/app/state/state.ts";
+import ValidatingInputBigIntField from "@/app/validating-input-bigint-field.tsx";
 
 export default function InputForm({state, dispatch}: {
     state: State,
@@ -17,7 +18,7 @@ export default function InputForm({state, dispatch}: {
 }) {
     const [width, setWidth] = useState(10);
     const [height, setHeight] = useState(10);
-    const [id, setId] = useState<number>();
+    const [id, setId] = useState<bigint>();
     const [algorithm, setAlgorithm] = useState('wilson');
 
     const handleSubmit = (e: FormEvent) => {
@@ -54,15 +55,14 @@ export default function InputForm({state, dispatch}: {
             value: numberValue
         };
     };
-    const validateIdInput: ValidatorFunction<string, number> = value => {
+    const validateIdInput: ValidatorFunction<string, bigint> = value => {
         if ("" === value) {
             return {
                 valid: true
             };
         }
-        const numberValue = Number(value);
-        // FIXME doesn't handle strings with characters correctly (e.g. "asdf" yields an empty value, due to "type=number").
-        if (isNaN(numberValue) || Math.floor(numberValue) !== numberValue) {
+        const numberValue = BigInt(value);
+        if (numberValue.toString() !== value.trim()) {
             return {
                 valid: false,
                 message: "Must be empty or an integer"
@@ -96,7 +96,7 @@ export default function InputForm({state, dispatch}: {
                                             disabled={state.loading}
                                             onChange={setHeight}
                 />
-                <ValidatingInputNumberField id={"id"}
+                <ValidatingInputBigIntField id={"id"}
                                             label={"ID (optional)"}
                                             value={id}
                                             validatorFn={validateIdInput}
diff --git a/src/app/validating-input-bigint-field.tsx b/src/app/validating-input-bigint-field.tsx
new file mode 100644
index 0000000..572f481
--- /dev/null
+++ b/src/app/validating-input-bigint-field.tsx
@@ -0,0 +1,60 @@
+import React, {ChangeEventHandler, useState} from 'react';
+
+export default function ValidatingInputBigIntField({
+                                                       id,
+                                                       label,
+                                                       value = 0n,
+                                                       constraints = undefined,
+                                                       validatorFn = (value) => {
+                                                           return {valid: true, value: BigInt(value)};
+                                                       },
+                                                       disabled = false,
+                                                       onChange = () => {
+                                                       }
+                                                   }:
+                                                   {
+                                                       id: string;
+                                                       label: string;
+                                                       value?: bigint;
+                                                       constraints?: { min?: bigint; max?: bigint; };
+                                                       validatorFn: ValidatorFunction<string, bigint>;
+                                                       disabled: boolean;
+                                                       onChange: (v: bigint) => void;
+                                                   }) {
+    const [error, setError] = useState<string>();
+
+    const handleValueChange: ChangeEventHandler<HTMLInputElement> = (e) => {
+        const value = e.target.value;
+        const validation = validatorFn(value);
+        if (!validation.valid) {
+            setError(validation.message);
+        } else {
+            setError(undefined);
+        }
+        if (validation.value !== undefined) {
+            onChange(validation.value);
+        }
+    };
+    return (
+        <>
+            <label htmlFor={id}>{label}: </label>
+            <input id={id}
+                   type={"number"}
+                   onChange={handleValueChange}
+                   value={value?.toString() || ""}
+                   min={constraints?.min?.toString()}
+                   max={constraints?.max?.toString()}
+                   disabled={disabled}
+            />
+            <span>{error}</span>
+        </>
+    );
+}
+
+export interface Validation<T> {
+    valid: boolean;
+    message?: string;
+    value?: T;
+}
+
+export type ValidatorFunction<I, T> = (v: I) => Validation<T>;
diff --git a/tsconfig.json b/tsconfig.json
index ef36eec..b953079 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
 {
   "compilerOptions": {
-    "target": "ES2017",
+    "target": "ES2020",
     "lib": ["dom", "dom.iterable", "esnext"],
     "allowJs": true,
     "allowImportingTsExtensions": true,