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...
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?
16 Answers
The API provides component for this purpose.
<Button
variant="contained"
component="label"
>
Upload File
<input
type="file"
hidden
/>
</Button>
newer MUI version:
<input
accept="image/*"
className={classes.input}
style={{ display: 'none' }}
id="raised-button-file"
multiple
type="file"
/>
<label htmlFor="raised-button-file">
<Button variant="raised" component="span" className={classes.button}>
Upload
</Button>
</label>
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' }}
id="raised-button-file"
multiple
type="file"
/>
<label htmlFor="raised-button-file">
<Button variant="raised" component="span" className={classes.button}>
Upload
</Button>
</label>
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}
id="icon-button-photo"
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}
id="icon-button-video"
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);
It is work for me ("@material-ui/core": "^4.3.1"):
<Fragment>
<input
color="primary"
accept="image/*"
type="file"
onChange={onChange}
id="icon-button-file"
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>
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>
);
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}
id="faceImage"
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/*" id="contained-button-file" multiple type="file" />
<Button variant="contained" component="span">
Upload
</Button>
</label>
<label htmlFor="icon-button-file">
<Input accept="image/*" id="icon-button-file" 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
id="import-button"
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" id="sampleFile" style="display: none;" />
<Button htmlFor="sampleFile" component="label" type={'submit'}>Upload</Button>
</form>
<input type="file"
id="fileUploadButton"
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/*" id="upload-company-logo" 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)