The last 30km of the 140km stage were flat, offering opportunities for the top sprinters and it was Demare who powered across the line fractionally ahead of Sagan.
Slovakia's Sagan had to settle for second place again as he did on stage two. Italian Davide Ballerini was third.
Portugal's Joao Almeida (Deceuninck-QuickStep) retained the pink jersey and opened a two-second lead over Ecuadorean Jonathan Caicedo courtesy of picking up a couple of bonus points in an intermediate sprint 25km from the finish line.
Earlier in the day, pre-race favourite Geraint Thomas abandoned the event after his crash in the neutral zone on Tuesday (AEDT) left him with a fractured pelvis.
"It's so frustrating," Thomas said.
"I'd put so much work into this race. I did everything I could and felt like I was in just as good, if not better, shape than when I won the Tour.
"So for it just to end like this is gutting," added the 34-year-old Welshman who claimed the Tour de France title in 2018.
Sagan's Bora-Hansgrohe piled on the pressure in the day's main climb, the Portella Mandrazzi, hoping to soften up the Slovakian's main sprint rivals.
It worked to some extent but there was still enough threat to Sagan in the final sprint, as Groupama-FDJ's Demare showed.
"I wasn't sure that I'd won when I crossed the line. It was so tight. It's fantastic to have won a Giro stage already in the first days of the race," said the Frenchman, who also won a Giro stage last year.
Sagan now has an incredible 30 second-place stage finishes in Grand Tours, along with 16 stage victories.
"Today, I went all out for the win. I didn't know who was first, second, or third, we all came to the line together and we had to wait for the photo-finish," he said.
"At times, I won by a few centimetres, and at times it was like today."
There was a freak crash at the end of the stage when a barrier - apparently blown over by the draft from a helicopter - brought down Italian Luca Wackermann and Dutchman Etienne van Empel, both of whom ride for Italy's Vini Zabu-KTM team.
Wackermann was taken to hospital in an ambulance.