Barca's new signing from Manchester City Ferran Torres was handed his debut, alongside recalls for Frenkie De Jong and Ronald Araujo while Madrid had just two changes, at the back as Nacho and Dani Carvajal were both recalled.
The game got off to a bright start, with Carlo Ancelotti's charges getting the better chances on counter-attacks but not precise enough in the final third.
However, Los Blancos' transitional attack finally yielded a goal in the 25th minute after Vinicius Junior held his composure from Karim Benzema's through ball and planted a strike into the top corner.
Xavi's troops were threatening to get back into the game, with Ousmane Dembele in particular causing problems and it was his cross that created the equaliser just before the half-time break, as Eder Militao's attempted clearance came off Luuk de Jong and went past goalkeeper Thibaut Courtois.
Xavi made a tactical change in the 65th minute, as Anssumane Fati returned from injury to replace Luuk De Jong while Ancelotti opted for fresh legs in attack in the same moment by bringing on Rodrygo in place on Marco Asensio.
Benzema eventually found the back of the net 18 minutes before time with a close-range strike from the rebound after shot-stopper Marc-Andre ter Stegen parried Vinicius Junior's shot into his path.
However, Fati snatched the equaliser for Blaugrana seven minutes before time with a close-range header at the back post after a cross by Jordi Alba, sending the game to extra-time.
Madrid remained a threat on counter-attacks early in extra-time and Federico Valverde eventually restored their lead eight minutes in with a composed finish in the box after a cut-back by Rodrygo.
Barca were fired up and came close twice minutes thereafter but both Sergio Busquets and Dembele were denied by Courtois in a double-save.
However, Xavi's men were struggling to carve out clear chances in the closing stages of extra-time, as Los Blancos they held on in the end to progress to the final against the winner between Atletico Madrid and Athletic Bilbao.