Figlio di Rossella e Silvano,[1] incomincia a lavorare agli studi di Cinecittà come stuntman fra il 2005 e il 2007, per poi passare a piccoli ruoli in sceneggiati televisivi[2].
Esordisce al cinema nel 2011 con il film Cinque; ottiene poi i ruoli da co-protagonista in Roma criminale (2013, nella parte di Marco Lanzi), Suburra (2015, con il ruolo di Aureliano Adami detto "Numero 8") e Non essere cattivo (2015, presentato fuori concorso alla 72ª Mostra internazionale d'arte cinematografica di Venezia, in cui interpreta Vittorio): con quest'ultima interpretazione vince il premio Nuovo Imaie Talent Award come miglior attore italiano esordiente[3] e viene nominato come miglior attore protagonista ai David di Donatello 2016. Nel 2015 gli viene inoltre consegnato da Francesco Fiumarella, autore del Premio Internazionale "Vincenzo Crocitti", il riconoscimento come miglior attore e rivelazione dell'anno.
Nel 2018 interpreta il ruolo di Stefano Cucchi in Sulla mia pelle di Alessio Cremonini (anch'esso una produzione Netflix) con cui vince il Premio Pasinetti speciale al film e ai migliori attori, il Premio Brian, il Premio FEDIC alla 75ª Mostra internazionale d'arte cinematografica di Venezia e il David di Donatello come miglior attore protagonista.[7][8] Nell'occasione ha dichiarato come il premio da lui ricevuto "sia di Stefano Cucchi"[9]. In seguito è protagonista del kolossal italiano prodotto da Rai Cinema, Il primo re[10], diretto da Matteo Rovere ed interpretato assieme ad Alessio Lapice, versione rivisitata del mito di Romolo e Remo, distribuito nel circuito cinematografico dal 31 gennaio 2019, e della commedia Di tutti i colori di Max Nardari, uscita in Italia il 18 giugno 2019 (dopo esser stata distribuita in Russia nel 2017 col titolo Liubov pret-a-porter)[11]. Nel marzo del 2019 è il protagonista del videoclip di Lunedì, canzone del rapper italiano Salmo.
Nel 2020 è protagonista della co-produzione internazionale Diavoli, serie televisiva tratta dall'omonimo romanzo di Guido Maria Brera ed ambientata nel mondo dell'alta finanza londinese, co-prodotta e trasmessa da Sky Atlantic e girata in lingua inglese, in cui è affiancato da Patrick Dempsey e Kasia Smutniak. Nella versione italiana l'attore per sua scelta ha deciso di non auto-doppiarsi[12] (recita infatti con la voce di Andrea Mete). Nel 2021 affianca Jasmine Trinca nel film Supereroi. Nell'aprile 2023 dichiara di essere affetto dalla sindrome di Tourette[13]. L'anno seguente interpreta Rocco Siffredi in Supersex, serie Netflix della quale è anche produttore associato.