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

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

You Might Also Like