Mikel Oyarzabal followed up his pair of semi-final assists against Italy with Spain’s opening goal, but Karim Benzema quickly hit back before Mbappe’s winner.
It was a second successive comeback win for Didier Deschamps’s side after their thrilling 3-2 semi-final success against Belgium.
Paul Pogba and Benzema linked up well early on, but Cesar Azpilicueta prevented the latter from setting up Mbappe.
Hugo Lloris saved easily from Pablo Sarabia at the other end, while Azpilicueta again thwarted Benzema with a key block.
France defender Jules Kounde survived a VAR referral for a possible handball in the penalty area and Marcos Alonso then curled a free-kick just wide with Lloris, expecting a cross, left helpless.
Les Bleus lost defender Raphael Varane to injury just before half-time, in a blow to his club side Manchester United. Dayot Upamecano took his place.
An error from Kounde allowed Sarabia to race clear down the flank, but his cross eluded Oyarzabal and Ferran Torres. A half-chance to Mbappe was then snuffed out by Alonso before the game burst into life in a dramatic three minutes.
Theo Hernandez, who scored France’s late winner against Belgium on Thursday, was the width of the crossbar away from repeating the feat, but instead Spain broke up the other end and Oyarzabal latched on to Sergio Busquets’s long pass, took on Upamecano and scored in the 64th minute.
However, France were level two minutes later as Pogba and Mbappe combined to create the chance and Benzema curled home brilliantly from a narrow angle.
Mbappe shot off target and had another effort blocked by Aymeric Laporte, while Unai Simon saved from Pogba as Deschamps’s side took the upper hand.
And they got their full reward when Mbappe delayed his run to race on to Hernandez’s through-ball and finish clinically beneath Simon, with the goal upheld by VAR after a check for offside.
Lloris saved brilliantly from Oyarzabal’s volley, while Simon denied Mbappe a third which would have been harsh on Spain.
Simon was forward for corners in the five added minutes but Lloris’s save from substitute Yeremi Pino snuffed out the last Spanish hope.
Italy earlier beat Belgium 2-1 in the third-place play-off, with Domenico Berardi’s penalty proving decisive.
After Thibaut Courtois denied Federico Chiesa in another goalless first half, Nicolo Barella volleyed the Azzurri in front in the first minute of the second period and Berardi doubled their advantage after Leicester’s Timothy Castagne fouled Chiesa.
Belgium hit the woodwork through Alexis Saelemaekers in the first half and Michy Batshuayi and Yannick Carrasco in the second but had to wait until the 86th minute to pull one back through Charles De Ketelaere’s first international goal.