How to enable file upload on React's Material UI simple input?
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?
316 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> </> );
}; 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)