The battle for position was frantic, and BMC Racing and Bora-hansgrohe traded places on the run-in to, and the lower slopes of, the final climb.
Nicolas Roche (BMC Racing) led the way under the flamme rouge, with the front of the race boxed out by the GC contenders as much as the favourites for the stage.
As Roche peeled off, Richie Porte accelerated but Sagan charged up to lead into the final few hundred metres, overcoming his slight slip on the pedals to beat Matthews with a big throw to the line.
And the Slovakian insisted he had not felt the pressure prior to the stage, having been marked as favourite for the finish.
“What is pressure?” Sagan laughed after the finish line. “First I need to thank all of my team – they did an amazing job pulling all day in the front.
“There was a lot of stress in the peloton then in the end it was a pretty hard climb.
“[When] I started my sprint, and pulled my foot out of my cleat and thought, what is happening today? But I am very happy for the win.”
Overall, Thomas now leads Froome and Matthews by 12 seconds overall, and with stage four another flat run to Vittel, the Team Sky duo will be hopeful of keeping those top two places.
Tour de France 2017: stage three – result
1) Peter Sagan (SVK) – Bora-hansgrohe – 5.07.19hrs
2) Michael Matthews (AUS) – Team Sunweb – ST
3) Daniel Martin (IRL) – QuickStep Floors
4) Greg van Avermaet (BEL) – BMC Racing
5) Alberto Bettiol (ITA) – Cannondale-Drapac +2″
6) Arnaud Demare (FRA) – FDJ – ST
7) Jakob Fuglsang (DEN) – Astana
8) Geraint Thomas (GBR) – Team Sky
9) Chris Froome (GBR) – Team Sky
10) Rafal Majka (POL) – Bora-hansgrohe
1) Geraint Thomas (GBR) – Team Sky – 10.00.31hrs
2) Chris Froome (GBR) – Team Sky +12″
3) Michael Matthews (AUS) – Team Sunweb – ST
4) Peter Sagan (SVK) – Bora-hansgrohe +13″
5) Edvald Boasson Hagen (NOR) – Dimension Data +16″
6) Pierre Latour (FRA) – Ag2r-La Mondiale +25″
7) Philippe Gilbert (BEL) – QuickStep Floors +30″
8) Michal Kwiatkowski (POL) – Team Sky +32″
9) Tim Wellens (BEL) – Lotto-Soudal – ST
10) Nikias Arndt (GER) – Team Sunweb +34″