Could not find a way to present an upload file button/input using the components provided here (great library btw!)
I've been using this and it seems to work well, perhaps a cleaner version could be included:
function UploadButton({label, onUpload, id}) {
let fileInput = null;
// If no id was specified, generate a random one
const uid = id || Math.random().toString(36).substring(7);
return (
<span>
<label htmlFor={uid} className="ui icon button">
<i className="upload icon"></i>
{label}
</label>
<input type="file" id={uid}
style={{display: "none"}}
onChange={() => {
onUpload(fileInput.files[0]);
}}
ref={input => {
fileInput = input;
}}
/>
</span>
);
}
I think a file button is a bit too specific, the idea you've used is a solution.
For those wishing to accomplish this, the following works:
<Label
as="label"
basic
htmlFor="upload"
>
<Button
icon="upload"
label={{
basic: true,
content: 'Select file(s)'
}}
labelPosition="right"
/>
<input
hidden
id="upload"
multiple
type="file"
/>
</Label>
Produces:

Note that the Label's as="label" (why is this not the default??!) htmlFor is the magic that makes it all work: The browser natively triggers an input when its associated label is clicked. A label can be associated in 2 ways:
for (htmlFor) attribute with a value equal to the input's name or idThe first does not work here because there are multiple form elements within the label; unfortunately that means relying on the second, which is not as good for accessibility and is more brittle (requires manually ensuring the input's name and the label's for stay synced).
I used a Button for its cursor: pointer (and because it fits nicely with the icon).
If you really wanted to go the whole 9 yards, you'd add an onChange to the input[type=file] to track the selected files and list their names below the button within the Label (which is what the native one does).
@jshado1 Dose not work properly, clicking upload icon submits form.
Adding type="button" did not helped.
Adding e.preventDefault() blocks file selection menu.
There should be official guide to do this properly.
@mjasnikovs I didn't experience that behaviour, although I am using it in a form with another button that is explicitly type="submit", so maybe that's why. I did notice after I posted this (I swear it wasn't happening before) that clicking the upload icon (which is the actual button) caused the file browser to not open; since I didn't want to actually kill the click event entirely (just make the button get out of the way), I added a className to the Label and then the following less:
.file-uploader {
.ui.input { display: none; }
// I ended up switching the `input` to an SUIR `Input`
// which doesn't accept the `hidden` attribute 馃槱
button.ui.button { pointer-events: none; }
// tells the browser to ignore button for all MouseEvents (doesn't affect :hover, etc)
// so the click hits the containing label instead
}
Yes, at the very least, there should be an example of this in the docs. However, I think it deserves its own component. I ended up extracting this into its own within my project (and added support for passing most of the values as props, plus fluid and onChange鈥攆luid was a bit tricky because it had to cascade to SUIR components that don't strictly support it but behave properly if passed the fluid css class).
I would be happy to contribute it back to SUIR if they're interested.
@jshado1 Hmm, i have it inside form group, with explicit submit button to. Maybe browser specific reaction.
I did it like this, not as fancy, but works.
<Label width="4" as="label" htmlFor="file" size="big">
<Icon name="file" />
Image
</Label>
<input id="file" hidden type="file" />
Just require CSS style for cursor.
@mjasnikovs interesting. I just checked in Chrome (which I used before), and $0.type indeed returns "submit" (although it's not present in the html). This is apparently the default鈥擨 thought the default was "button" (source: MDN); however, SUIR doesn't seem to support specifying the type for <Button>, or at least not with all of the other props I've set (SUIR applies the type="button" to the container div instead of the button itself).
based on @jshado1's suggestion i came up with this component. (in typescript, but you can remove unnecessary types) I just declared the Button as label and removed the surrounding label. That should also solve the submit problem. Furthermore you can now treat the component as a normal semantic Button since all props are passed through. For my purpose I also added an onSelect handler:
import * as React from 'react';
import { Component } from 'react';
import { Button, ButtonProps, Label } from 'semantic-ui-react';
import * as uuid from 'uuid';
interface ActionProps {
onSelect?: (file) => void;
}
export class FileButton extends Component<ActionProps & ButtonProps> {
private id: string = uuid.v1();
constructor(props) {
super(props);
this.onChangeFile = this.onChangeFile.bind(this);
}
public render() {
return (
<React.Fragment>
<Button
{...this.props}
as="label"
htmlFor={this.id} />
<input
hidden
id={this.id}
multiple
type="file"
onChange={this.onChangeFile} />
</React.Fragment>
);
}
private onChangeFile() {
const fileButton: any = document.getElementById(this.id);
const file = fileButton ? fileButton.files[0] : null;
if (this.props.onSelect) {
this.props.onSelect(file);
}
}
}

@strrel try this one
The original is a typescript snippet
import * as React from 'react';
import { Component } from 'react';
import { Button, ButtonProps, Label } from 'semantic-ui-react';
import * as uuid from 'uuid';
export class FileButton extends Component {
constructor(props) {
super(props);
this.id = uuid.v1();
this.onChangeFile = this.onChangeFile.bind(this);
}
render() {
return (
<div>
<Button
{...this.props}
as="label"
htmlFor={this.id} />
<input
hidden
id={this.id}
multiple
type="file"
onChange={this.onChangeFile} />
</div>
);
}
onChangeFile() {
const fileButton = document.getElementById(this.id);
const file = fileButton ? fileButton.files[0] : null;
if (this.props.onSelect) {
this.props.onSelect(file);
}
}
}
export default FileButton;
@iad42 Thank you!
It's rare that I come across a solution that was recently posted to an issue I'm looking for in the moment. I'm not sure how much time you just saved me, but I'm sure at the very least I gained a day in development haha. Cheers! 馃
I wonder why those solutions are working for me even without the use of htmlFor and id. Tried with Safari and Chrome, maybe I need it for IE?
Also, I just wanted to mention that instead of using document.getElementById to get the files, the onChangeFile receives an event which contains the files in event.target.files
Instead of relying on ID for this, you can use ref and call .click().
let fileInput: HTMLInputElement;
return (
<Button
as="label"
title="Add another video file"
onClick={() => fileInput.click()}
>
<Icon.Group>
<Icon name="video" />
<Icon corner name="add" />
</Icon.Group>
</Button>
<input
ref={element => fileInput = element}
hidden
type="file"
/>
);
This is safer as there's no risk of ID clashes
@Jameskmonger's solution is great. i did a refactor that i think blends the best of all of the above solutions. it's extensible, provides good defaults, and mandates essential props (e.g. id for the input so the label/accessibility works).

export const InputFile: React.FC<{
button?: ButtonProps
input: React.InputHTMLAttributes<any> & { id: string }
}> = ({ button = {}, input: inputProps }) => {
let hiddenInput: HTMLInputElement | null = null
return (
<React.Fragment>
<Button
icon='upload'
htmlFor={inputProps.id}
label={<Label as='label' style={{ cursor: 'pointer' }} basic children='Select file' />}
onClick={() => hiddenInput!.click()}
labelPosition='right'
{...button}
/>
<input
hidden
ref={el => {
hiddenInput = el!
}}
type='file'
{...inputProps}
/>
</React.Fragment>
)
}
usage:
<InputFile
input={{
id: 'upload-file',
onChange: handleUploadRequest
}}
/>
and i released https://www.npmjs.com/package/semantic-ui-react-input-file for it. perhaps we can collab :)
@cdaringe What does the ! do here in hiddenInput!.click() or hiddenInput = el!?
It's a TS-ism that asserts that the operand is non-null/non-undefined.
After looking for options, this is what I finally come up with (plain React no TS)
<Button as='div' labelPosition='right' onClick={() => this.input.click()}>
<Button icon title="Add another file">
<Icon name="upload"/>
</Button>
<Label as='a' basic pointing='left'>
Upload CSV
</Label>
</Button>
<input
ref={element => this.input = element}
hidden
onChange={(e)=>this.onChange(e)}
type="file"
/>

@laurensiusadi more like "plain React no BS"
Most helpful comment
@strrel try this one
The original is a typescript snippet