Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

cib88's avatar
Level 2

File upload error inertia.js and react.js

I'm trying to upload an image along with some content using Inertia & React. The content I can upload fine however I'm getting an error while trying to upload the image.

{"image":"The image must be an image."}

EDIT: after debugging for a bit I think it's more typescript issue than inertia.

My main form component / page:

interface Props {
    auth: any;
}

interface FormProps {
    title: string;
    intro: string;
    image: FileList | undefined;
    published_date: string;
}

const Create = ({ auth }: Props) => {
    const { data, setData, processing, errors } = useForm<FormProps>({
        title: "",
        intro: "",
        image: undefined,
        published_date: "",
    });

    const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
        let file = e.target.files;

        if (file !== undefined) {
            setData("image", file);
        }
    };

    const submit = (e: React.SyntheticEvent) => {
        e.preventDefault();
        {
            console.log(data);
        }
        Inertia.post("admin.blogs.store");
    };

    return (
        <Authenticated auth={auth}>
            <Container>
                <Heading type={"h3"}>Create a post</Heading>

                <form onSubmit={submit} noValidate>
                    <Row layout="inline">
                        <Label input={"title"} value={"Title"} required={true}>
                            <Input
                                type="text"
                                name="title"
                                value={data.title}
                                required={true}
                                onChange={(e) =>
                                    setData("title", e.target.value)
                                }
                            />
                            {errors.title && (
                                <InputError error={errors.title} />
                            )}
                        </Label>

                        <Label
                            input={"published_date"}
                            value={"Date"}
                            required={true}
                        >
                            <Input
                                type="date"
                                name="published_date"
                                value={data.published_date}
                                required={true}
                                onChange={(e) =>
                                    setData("published_date", e.target.value)
                                }
                            />
                            {errors.published_date && (
                                <InputError error={errors.published_date} />
                            )}
                        </Label>
                    </Row>

                    <Row>
                        <Label input={"intro"} value={"Intro"} required={true}>
                            <Textarea
                                name="intro"
                                value={data.intro}
                                required={true}
                                onChange={(e) =>
                                    setData("intro", e.target.value)
                                }
                            />
                            {errors.intro && (
                                <InputError error={errors.intro} />
                            )}
                        </Label>
                    </Row>

                    <Row>
                        <Label input={"image"} value={"Image"} required={true}>
                            <Input
                                type="file"
                                name="image"
                                value={data.image}
                                required={true}
                                onChange={handleFile}
                            />
                            {errors.image && (
                                <InputError error={errors.image} />
                            )}
                        </Label>
                    </Row>
                    <Row>
                        <Button
                            processing={processing}
                            type={"submit"}
                            color={"primary"}
                        >
                            Publish Blog
                        </Button>
                    </Row>
                </form>
            </Container>
        </Authenticated>
    );
};

export default Create;

My Input component:

const Input = styled("input", {
    width: "100%",
    height: "45px",
    background: "$white",
    color: "$black",
    textIndent: "10px",
    border: "1px solid $grey",
    borderRadius: "5px",
    paddingRight: "10px",
});

interface Props {
    type: string;
    name: string;
    value: string;
    required?: boolean;
    onChange: React.ChangeEventHandler<HTMLInputElement>;
}

export default ({ type, name, value, required, onChange }: Props) => {
    const input = useRef<HTMLInputElement>(null);

    return (
        <Input
            type={type}
            name={name}
            value={value}
            ref={input}
            required={required}
            onChange={(e) => onChange(e)}
        />
    );
};

I get a compile error of Type 'FileList' is not assignable to type 'string'.

When I try to upload an image I get the following error in my console

Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
    at HTMLInputElement.set [as value]

If I add FileList to the value type on my input like this value: string | FileList; I then get the following errors.

TS2769: No overload matches this call.
  Overload 1 of 3, '(props: Omit<Pick<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof InputHTMLAttributes<...>> & { ...; }, "css" | "as"> & TransformProps<...> & { ...; }): ReactElement<...>', gave the following error.
    Type 'string | FileList' is not assignable to type 'string | number | readonly string[]'.
      Type 'FileList' is not assignable to type 'string | number | readonly string[]'.
        Type 'FileList' is missing the following properties from type 'readonly string[]': concat, join, slice, indexOf, and 17 more.
  Overload 2 of 3, '(props: Omit<Pick<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof InputHTMLAttributes<...>> & { ...; }, "css" | "as"> & TransformProps<...> & { ...; }): ReactElement<...>', gave the following error.
    Type 'string | FileList' is not assignable to type 'string | number | readonly string[]'.
  Overload 3 of 3, '(props: Omit<Pick<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof InputHTMLAttributes<...>> & { ...; }, "css"> & TransformProps<...> & { ...; }): ReactElement<...>', gave the following error.
    Type 'string | FileList' is not assignable to type 'string | number | readonly string[]'.
0 likes
5 replies
anilkumarthakur60's avatar
import React from 'react'
import { useForm } from '@inertiajs/inertia-react'

const { data, setData, post, progress } = useForm({
  image: null,
})

function submit(e) {
  e.preventDefault()
  post('/users')
}

return (
  <form onSubmit={submit}>

    <input type="file" value={data.image} onChange={e => setData('image', e.target.files[0])} />


    {progress && (
      <progress value={progress.percentage} max="100">
        {progress.percentage}%
      </progress>
    )}
    <button type="submit">Submit</button>
  </form>
)
cib88's avatar
Level 2

@anilkumarthakur60 I tried that but then my app crashes and I get Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string. at HTMLInputElement.set [as value]

cib88's avatar
Level 2

@kaiserkais I did yeah, see below for working code. The issue was the props for my input component. In the end I ended up extending the InputHTMLAttributes like this

interface Props extends React.InputHTMLAttributes<HTMLInputElement> {
    value?: string;
    onChange: React.ChangeEventHandler<HTMLInputElement>;
}

Form Component

interface FormProps {
    title: string;
    intro: string;
    image: File | undefined;
    published_date: string;
}

const Create = ({ auth }: Props) => {
    const { data, setData, processing, post, errors } = useForm<FormProps>({
        title: "",
        intro: "",
        image: undefined,
        published_date: "",
    });

    const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
        if (e.currentTarget.files) {
            setData("image", e.currentTarget.files[0]);
        }
    };

    const onSubmit = (e: React.SyntheticEvent) => {
        e.preventDefault();
        post("/admin/blogs/create", {
            preserveScroll: true,
        });
    };

    return (
        <Authenticated auth={auth} css={{ background: "$light" }}>
            <Container css={{ maxWidth: "1200px", py: "100px" }}>
                <Heading size="charlie">Create a post</Heading>
                <Errors errors={errors} />

                <form onSubmit={onSubmit} noValidate>
                    <Row layout="inline">
                        <Label input={"title"} value={"Title"} required={true}>
                            <Input
                                type="text"
                                name="title"
                                value={data.title}
                                required={true}
                                onChange={(e) =>
                                    setData("title", e.target.value)
                                }
                            />
                            {errors.title && (
                                <InputError error={errors.title} />
                            )}
                        </Label>

                        <Label
                            input={"published_date"}
                            value={"Date"}
                            required={true}
                        >
                            <Input
                                type="date"
                                name="published_date"
                                value={data.published_date}
                                required={true}
                                onChange={(e) =>
                                    setData("published_date", e.target.value)
                                }
                            />
                            {errors.published_date && (
                                <InputError error={errors.published_date} />
                            )}
                        </Label>
                    </Row>

                    <Row>
                        <Label input={"intro"} value={"Intro"} required={true}>
                            <Textarea
                                name="intro"
                                value={data.intro}
                                required={true}
                                onChange={(e) =>
                                    setData("intro", e.target.value)
                                }
                            />
                            {errors.intro && (
                                <InputError error={errors.intro} />
                            )}
                        </Label>
                    </Row>

                    <Row>
                        <Label input={"image"} value={"Image"} required={true}>
                            <Input
                                type="file"
                                name="image"
                                required={true}
                                onChange={handleFile}
                            />
                            {errors.image && (
                                <InputError error={errors.image} />
                            )}
                        </Label>
                    </Row>
                    <Row>
                        <Button
                            processing={processing}
                            type={"submit"}
                            color={"primary"}
                        >
                            Publish Blog
                        </Button>
                    </Row>
                </form>
            </Container>
        </Authenticated>
    );
};

export default Create;

Input component is

interface Props extends React.InputHTMLAttributes<HTMLInputElement> {
    value?: any;
    onChange: React.ChangeEventHandler<HTMLInputElement>;
}

export default ({ type, name, value, required, onChange }: Props) => {
    const input = useRef<HTMLInputElement>(null);

    return (
        <Input
            type={type}
            name={name}
            value={value}
            ref={input}
            required={required}
            onChange={(e) => onChange(e)}
        />
    );
};
nichtkunst's avatar

don't use null, use "" instead

useForm<FormProps>({
        title: "",
        intro: "",
        image: "",
        published_date: "",
    });

Please or to participate in this conversation.