100

How can I set the focus on a Material UI TextField component?

componentDidMount() {
  ReactDom.findDomNode(this.refs.myControl).focus()
}

I have tried the above code, but it does not work.

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
Giles Bradshaw
  • 1,249
  • 2
  • 9
  • 8
  • Added a related answer to [another SO question](https://stackoverflow.com/questions/42459190/how-to-use-react-refs-to-focus-a-redux-form-field/53372707#53372707). – Greg K Nov 19 '18 at 10:41

14 Answers14

117

For React 16.8.6, you should use the inputRef property of TextField to set focus.

<TextField
  inputRef={input => input && input.focus()}
/>

Material UI doc says:

inputRef: Use this property to pass a ref callback to the native input component.

The ref prop wouldn't work. It gives a reference to the root DOM element of the TextField which is the parent of the <input> element.

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
AlienKevin
  • 2,691
  • 2
  • 17
  • 19
  • 7
    This is the correct solution. Remember, is a combination of several other Material components, including , , , etc. – Sterling Bourne Aug 26 '19 at 14:29
  • 2
    This is very short for a correct answer, please add some minimal working example. – Geeocode Oct 18 '20 at 00:56
  • That's great, but for some reason sometimes I get `null` for `input` and have to check it. Why do I get `null`? – lazy.lizard Mar 10 '21 at 09:15
  • This works but tests give error saying- Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops –  Kimaya Apr 06 '21 at 19:56
  • 1
    @lazy.lizard surely you have found out by now, but that's just basic react refs: when the component mounts, the ref value is a DOM element, but it is null when the component unmounts. – loopmode Jul 14 '21 at 18:57
102

You can use the autoFocus prop:

<TextField value="some value" autoFocus />

It sets the autofocus attribute on the <input> element.

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
Antonis Zisis
  • 1,943
  • 3
  • 16
  • 17
34

autoFocus was also not working for me, perhaps since this is a component that's not mounted when the top-level component loads. I had to do something a lot more convoluted to get it to work:

function AutoFocusTextField(props) {
  const inputRef = React.useRef();

  React.useEffect(() => {
    const timeout = setTimeout(() => {
      inputRef.current.focus();
    }, 100);

    return () => {
      clearTimeout(timeout);
    };
  }, []);

  return <TextField inputRef={inputRef} {...props} />;
}

Note that for some reason it does not work without the setTimeout. For more info see https://github.com/callemall/material-ui/issues/1594.

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
Lane Rettig
  • 6,640
  • 5
  • 42
  • 51
  • 1
    you should check if the input is available inside timeout callback. This code may throw. What would be even better is to save `setTimeout` returned id to component and on `componentWillUnmount` check if timeout is still there and clear it if so. – Lukas Liesis Apr 08 '18 at 19:03
  • You can use this.setState({}, () => { input.focus() }) instead of setTimeout – Weijing Jay Lin Apr 30 '19 at 09:39
  • 5
    For React 16.8.6, you should use the `inputRef` property of TextField to set focus. See [Material-ui doc](https://material-ui.com/api/text-field/#props) – AlienKevin May 09 '19 at 20:14
  • 2
    How is it not immediately obvious to the author of the code that this is an abhorrent hack? It should have been clear while typing `setTi...`. Just about there. **Don't copy this**, take a look at the AlienKevin's answer. – Johannes Pille May 09 '20 at 09:53
10

If you are using material-ui TextField and react functional component, you can pass inputRef in your TextField component. The trick here is the if condition if(input != null).

<TextField
    variant="filled"
    inputRef={(input) => {
      if(input != null) {
         input.focus();
      }
    }}
/>

Here is an working example for you. CodeSandBox- Material-ui-TextFieldFocus

atbrakhi
  • 156
  • 1
  • 7
8

This will focus the component every time it renders. Other solutions I tried only focus the element an initial time.

  const inputRef = React.useRef<HTMLInputElement>();
  
  useEffect(() => {
    inputRef.current?.focus();
  }, [inputRef.current]);

  const setTextInputRef = (element: HTMLInputElement) => {
    inputRef.current = element;
  };
   
  return (
    <TextField
      inputRef={setTextInputRef}
    />
a2f0
  • 1,615
  • 2
  • 19
  • 28
6

add this propery to your TextField component :

inputRef={(input) => input?.focus()}
anesboz
  • 412
  • 4
  • 10
  • if you want to focus only on condition use something like : inputRef={(input) => condition === true && input?.focus()} – anesboz Apr 24 '22 at 23:28
3
 const handleClick =  () => {
   inputRef.current.firstChild.focus();
   inputRef.current.firstChild.placeholder = '';
  }
  <InputBase
        value={value}
        ref={inputRef}
        placeholder="search" />
    <Button onClick={handleClick}>Click</Button>
2

useRef hook simple example:

const focusMe_Ref = useRef(null); // 1. create

useEffect(() => {
  focusMe_Ref.current.focus();  // 2. startup
}, []);


...

<TextField
   inputRef={focusMe_Ref} // 3. will focused
  ...
/>
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
Shawn Lee
  • 41
  • 4
1

This code is actually good, but has a drawback, on every render it's going to create a new function. It easily can be solved using useCallback

<TextField
  inputRef={input => input && input.focus()}
/>

Should be

const callbackRef = useCallback((inputElement) => {
     if (inputElement) {
         inputElement.focus();
     }
 }, []);
...
<TextField
  inputRef={callbackRef}
/>
Petr Schukin
  • 170
  • 2
  • 12
  • 2
    Using `useCallback` does not prevent a new function from being created on every render. it makes the value of `callbackRef` to always be the function from the first render, but all other renders will still create new functions that will not be used by anyone. – Ido Ofir Aug 17 '21 at 08:53
  • 1
    And as the cherry on the top you are also creating an additional array – hotpink Feb 04 '22 at 13:18
0

I am using this solution, works for text fields inspired by https://gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573


const useFocus = (): [any, () => void] => {
    const htmlElRef: MutableRefObject<any> = useRef<HTMLDivElement>();
    const setFocus = (): void => {
        if (!htmlElRef || !htmlElRef.current) return
        const div = htmlElRef.current as HTMLDivElement
        if (!div) return
        const input = div.querySelector("input")
        if (input) input.focus()
    }
    return [htmlElRef, setFocus];
};


export function MyComp() {
  const [ref, setFocus] = useFocus()
  
  // use setFocus() to focus the input field

  return <Input ref={ref} />
}

lcapra
  • 1,420
  • 16
  • 18
0

I had a similar problem where the input field didn't regain focus after I've modified its contents with external controls (an emoji picker). I ended up with this brute workaround hook:

const useEnforceFocusTextField = (textFieldRef: React.RefObject<HTMLInputElement>) => {
  const [enforcedRerender, setEnforcedRerender] = useState(false);
  React.useEffect(() => {
    textFieldRef.current?.focus();
    const timeout = setTimeout(() => {
      textFieldRef.current?.focus();
    }, 100);
    return () => clearTimeout(timeout);
  }, [enforcedRerender]);
  return () => setEnforcedRerender((n) => !n);
};

From the outside, you call utilize this hook in the following manner:

const textFieldRef = useRef<HTMLInputElement>(null);
...
// enforceFocus() can be called in any callback to set the focus to the textfield
const enforceFocus = useEnforceFocusTextField(textFieldRef);
...
return <TextField inputRef={textFieldRef} ... />
NotX
  • 1,516
  • 1
  • 14
  • 28
0

For TypeScript version in MUI 5 I had to use inner elements of TextField such a way

    const inputRef = useRef(null);
    ...
    useEffect(() => {
      const timeout = setTimeout(() => {
        if (inputRef && inputRef.current) {
          (((inputRef.current as unknown as HTMLElement)?.firstChild as HTMLElement)?.firstChild as HTMLElement)?.focus();      
        }
      }, 100);
      return () => {
        clearTimeout(timeout);
      };
    }, [open]);
    ...
    <TextField ref={inputRef}/>
-1

For a material ui TextField you need to input the props for autoFocus in a inputProps object like this.

 <TextField inputProps={{ autoFocus: true }} />
Stalli
  • 1
-2

AlienKevin is correct ( pass a ref callback to "TextField.inputProps" ), but you can also save the element reference on your "this" object, so that you can set focus later. Here is an example in Coffeescript:

TextField
    inputProps:
        ref: (el)=>
            if el?
                @input_element = el

Button
    onClick:=> 
        @input_element.focus()
Nick Perkins
  • 8,034
  • 7
  • 40
  • 40