The Conservatives have held the safe suburban London seat of Old Bexley and Sidcup in a by-election.
Their candidate, local councillor Louie French, won more than half the votes, with Labour’s Daniel Francis second.
Mr French paid tribute to the area’s former MP, James Brokenshire, whose death from cancer in October triggered the contest.
The Tories’ majority has been cut from nearly 19,000 to 4,478, with a 10% swing to Labour.
In an email to supporters, the new Conservative MP said party members “don’t have time to rest on our laurels” and urged them to donate funds to help with campaigning in the next by-election in two weeks’ time.
The North Shropshire seat was vacated by Owen Paterson, who resigned after breaking lobbying rules.
Although votes at mid-term polls are usually lower than at general elections, the 34% turnout on Thursday was the lowest at a by-election since 2018.
Reform UK – formerly the Brexit Party – came third with 6.6% of the vote, with the party’s leader and candidate Richard Tice describing it as a “massive result”.
The Green Party and Liberal Democrats both lost their deposits.
Bexley is a traditionally strong area for the Conservatives and the party has held the constituency in its current form since its creation in 1983.
Labour’s Ellie Reeves, MP for nearby Lewisham West and Penge, said she was “pleased with the result”.
“This is a Conservative stronghold, somewhere that had a 19,000 majority at the last general election and what we’ve seen tonight is that majority being slashed,” she added.
But Tory Party chairman Oliver Dowden told BBC Breakfast the reduced majority for his party “reflected a very low turnout”.
He described the victory as a “good solid result for us”, adding it was consistent with elections held in the middle of a Parliament.
Speaking to Sky News, he said: “This idea that Labour have made some surge ahead is really for the birds.”
by Ione Wells, BBC political correspondent
This Conservative hold hasn’t taken anyone by surprise. It’s a seat as “true blue” as they come.
Expectation management from the opposition parties has been going on for weeks – with all of them privately admitting the Tories’ 19,000-strong majority would be very difficult to overturn.
Labour are encouraged by the 10% swing to them, claiming that as a positive sign for more marginal seats in future elections.
But retaining more than half the votes is a good result for the Tories too.
Senior MPs who have knocked doors here in recent weeks all pointed out that governing parties used to get a hammering in midterm by-elections – seen by some voters as a free hit to send the government a message, without significantly shifting the power balance in Westminster.
The Lib Dems were not expecting a good result here at all, instead throwing all their resources at the North Shropshire by-election in two weeks’ time.
In that contest they hope to replicate their stunning victory in Chesham and Amersham, where they overturned a large Tory majority.
Tory nerves have been more tangible about that by-election – where all sides feel there is a lot more to play for.
The Bexley by-election was prompted by the death of former MP James Brokenshire, who died from lung cancer aged 53 in October.
At the 2019 general election, he held the south-east London seat with a majority of almost 19,000, taking 64.5% of the vote.
Wearing the rosette of his predecessor, Mr French paid an emotional tribute to him in his acceptance speech, adding that the contest had been tough but “fought with dignity”.
He vowed to “work tirelessly to repay the trust that you’ve placed in me and I will not let you down”.
“My focus will now be delivering on those promises that I made during the campaign – get our fair share of London’s police officers, securing more investment for local schools and hospitals, protecting our precious green spaces.”
Old Bexley and Sidcup: Result in full
Louie French, Conservatives – 11,189 (51.48%)
Daniel Francis, Labour – 6,711 (30.88%)
Richard Tice, Reform UK – 1,432 (6.59%)
Jonathan Rooks, Green – 830 (3.82%)
Simone Reynolds, Liberal Democrats – 647 (2.98%)
Elaine Cheeseman, The English Democrats – 271 (1.25%)
John Poynton, UKIP – 184 (0.85%)
Richard Hewison, Rejoin EU – 151 (0.69%)
David Kurten, Heritage Party – 116 (0.53%)
Carol Valinejad, Christian Peoples Alliance – 108 (0.50%)
Mad Mike Young, Official Monster Raving Loony Party – 94 (0.43%)