Velvet Star Monitor

Standout celebrity highlights with iconic style.

updates

How to enable file upload on React's Material UI simple input?

Writer Matthew Martinez

I am creating a simple form to upload file using electron-react-boilerplate with redux form & material ui.

The problem is that I do not know how to create input file field because material ui does not support upload file input.

Any ideas on how to achieve this?

3

16 Answers

The API provides component for this purpose.

<Button variant="contained" component="label"
> Upload File <input type="file" hidden />
</Button>
2

newer MUI version:

<input accept="image/*" className={classes.input} style={{ display: 'none' }} multiple type="file"
/>
<label htmlFor="raised-button-file"> <Button variant="raised" component="span" className={classes.button}> Upload </Button>
</label> 
5

You need to wrap your input with component, and add containerElement property with value 'label' ...

<RaisedButton containerElement='label' // <-- Just add me! label='My Label'> <input type="file" />
</RaisedButton>

You can read more about it in this GitHub issue.

EDIT: Update 2019.

Check at the bottom answer from @galki

TLDR;

<input accept="image/*" className={classes.input} style={{ display: 'none' }} multiple type="file"
/>
<label htmlFor="raised-button-file"> <Button variant="raised" component="span" className={classes.button}> Upload </Button>
</label> 
2

Here's an example using an IconButton to capture input (photo/video capture) using v3.9.2:

import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import IconButton from '@material-ui/core/IconButton';
import PhotoCamera from '@material-ui/icons/PhotoCamera';
import Videocam from '@material-ui/icons/Videocam';
const styles = (theme) => ({ input: { display: 'none' }
});
class MediaCapture extends Component { static propTypes = { classes: PropTypes.object.isRequired }; state: { images: [], videos: [] }; handleCapture = ({ target }) => { const fileReader = new FileReader(); const name = target.accept.includes('image') ? 'images' : 'videos'; fileReader.readAsDataURL(target.files[0]); fileReader.onload = (e) => { this.setState((prevState) => ({ [name]: [...prevState[name], e.target.result] })); }; }; render() { const { classes } = this.props; return ( <Fragment> <input accept="image/*" className={classes.input} onChange={this.handleCapture} type="file" /> <label htmlFor="icon-button-photo"> <IconButton color="primary" component="span"> <PhotoCamera /> </IconButton> </label> <input accept="video/*" capture="camcorder" className={classes.input} onChange={this.handleCapture} type="file" /> <label htmlFor="icon-button-video"> <IconButton color="primary" component="span"> <Videocam /> </IconButton> </label> </Fragment> ); }
}
export default withStyles(styles, { withTheme: true })(MediaCapture);
1

It is work for me ("@material-ui/core": "^4.3.1"):

 <Fragment> <input color="primary" accept="image/*" type="file" onChange={onChange} style={{ display: 'none', }} /> <label htmlFor="icon-button-file"> <Button variant="contained" component="span" className={classes.button} size="large" color="primary" > <ImageIcon className={classes.extendedIcon} /> </Button> </label> </Fragment>
0

If you're using React function components, and you don't like to work with labels or IDs, you can also use a reference.

const uploadInputRef = useRef(null);
return ( <Fragment> <input ref={uploadInputRef} type="file" accept="image/*" style={{ display: "none" }} onChange={onChange} /> <Button onClick={() => uploadInputRef.current && uploadInputRef.current.click()} variant="contained" > Upload </Button> </Fragment>
);
1

Nov 2020

With Material-UI and React Hooks

import * as React from "react";
import { Button, IconButton, Tooltip, makeStyles, Theme,
} from "@material-ui/core";
import { PhotoCamera } from "@material-ui/icons";
const useStyles = makeStyles((theme: Theme) => ({ root: { "& > *": { margin: theme.spacing(1), }, }, input: { display: "none", }, faceImage: { color: theme.palette.primary.light, },
}));
interface FormProps { saveFace: any; //(fileName:Blob) => Promise<void>, // callback taking a string and then dispatching a store actions
}
export const FaceForm: React.FunctionComponent<FormProps> = ({ saveFace }) => { const classes = useStyles(); const [selectedFile, setSelectedFile] = React.useState(null); const handleCapture = ({ target }: any) => { setSelectedFile(target.files[0]); }; const handleSubmit = () => { saveFace(selectedFile); }; return ( <> <input accept="image/jpeg" className={classes.input} type="file" onChange={handleCapture} /> <Tooltip title="Select Image"> <label htmlFor="faceImage"> <IconButton className={classes.faceImage} color="primary" aria-label="upload picture" component="span" > <PhotoCamera fontSize="large" /> </IconButton> </label> </Tooltip> <label>{selectedFile ? selectedFile.name : "Select Image"}</label>. . . <Button onClick={() => handleSubmit()} color="primary"> Save </Button> </> );
};

Official recommendation

import * as React from 'react';
import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import PhotoCamera from '@mui/icons-material/PhotoCamera';
import Stack from '@mui/material/Stack';
const Input = styled('input')({ display: 'none',
});
export default function UploadButtons() { return ( <Stack direction="row" alignItems="center" spacing={2}> <label htmlFor="contained-button-file"> <Input accept="image/*" multiple type="file" /> <Button variant="contained" component="span"> Upload </Button> </label> <label htmlFor="icon-button-file"> <Input accept="image/*" type="file" /> <IconButton color="primary" aria-label="upload picture" component="span"> <PhotoCamera /> </IconButton> </label> </Stack> );
}

You can use Material UI's Input and InputLabel components. Here's an example if you were using them to input spreadsheet files.

import { Input, InputLabel } from "@material-ui/core";
const styles = { hidden: { display: "none", }, importLabel: { color: "black", },
};
<InputLabel htmlFor="import-button" style={styles.importLabel}> <Input inputProps={{ accept: ".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel", }} onChange={onInputChange} style={styles.hidden} type="file" /> Import Spreadsheet
</InputLabel>
 import AddPhotoIcon from "@mui/icons-material/AddAPhoto"; import Fab from "@mui/material/Fab"; <Fab color="primary" aria-label="add-image" sx={{ position: "fixed", bottom: 16, right: 16, overflow: "hidden" }}> <input type="file" onChange={imageHandler} accept=".jpg, .jpeg, .png" accept="image/*" multiple style={{ //make this hidden and display only the icon position: "absolute", top: "-35px", left: 0, height: "calc(100% + 36px)", width: "calc(100% + 5px)", outline: "none", }} /> <AddPhotoIcon /> </Fab>

Just the same as what should be but change the button component to be label like so

<form id='uploadForm' action=' method='post' encType="multipart/form-data"> <input type="file" /> <Button htmlFor="sampleFile" component="label" type={'submit'}>Upload</Button>
</form>
<input type="file" style={{ display: 'none' }} onChange={onFileChange} /> <label htmlFor={'fileUploadButton'}> <Button color="secondary" className={classes.btnUpload} variant="contained" component="span" startIcon={ <SvgIcon fontSize="small"> <UploadIcon /> </SvgIcon> } > Upload </Button> </label>

Make sure Button has component="span", that helped me.

Here an example:

return ( <Box alignItems='center' display='flex' justifyContent='center' flexDirection='column'> <Box> <input accept="image/*" type='file' hidden /> <label htmlFor="upload-company-logo"> <Button component="span" > <Paper elevation={5}> <Avatar src={formik.values.logo} className={classes.avatar} variant='rounded' /> </Paper> </Button> </label> </Box> </Box> )

You can pursue all the comments above, those are really great, However, I have another option for customizing your component, if you want to follow.

// Import

import { styled } from '@mui/material/styles';
import { Input } from "@mui/material";

// Custom style

const CustomFileInput = styled(Input)(({ theme }) => { return { color: "white", '::before': { border: 'none', position: 'static', content: 'none' }, '::after': { border: 'none', position: 'static', content: 'none' } }
});

// Using that component

<CustomFileInput type="file" />

Both @galki and @elijahcarrel method works fine. If anyone trying to do unit-testing(jest) for these two answers.

You wont be able to use the button component with (specially if you are using disabled=true

expect(getByRole("button", {name: "Upload"})).not.toBeEnabled();

instead use this

expect(getByLabelText("Upload")).not.toBeEnabled();

This is for Select Image File

<IconButton color="primary" component="label"> <input type="file" accept="image/*" hidden /> <AttachFileIcon fontSize="medium" />
</IconButton>

NOTE : React Material UI Component (IconButton, AttachFileIcon)

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy