natbib has a sort option that allows you to sort \citep/\citet calls in the order the entries appear in the bibliography.
\documentclass[british]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage{csquotes}
\usepackage[sort]{natbib}
\bibliographystyle{plainnat}
\begin{filecontents}{\jobname.bib}
@book{aardvark,
author = {Anne Aardvark},
title = {Ardvarking},
year = {2020},
}
@book{zebra,
author = {Zoe Zebra},
title = {Zebraing},
year = {2010},
}
\end{filecontents}
\begin{document}
\citep{zebra,aardvark}
\citep{aardvark,zebra}
\bibliography{\jobname}
\end{document}
![[Aardvark, 2020, Zebra, 2010]//[Aardvark, 2020, Zebra, 2010]](../../images/0af0f7a26e3d7a5658261eb08473f5bb.webp)
If you want to sort citations differently than the bibliography, some additional work is needed. I have demonstrated this for apalike in In-text citation sorted according to publication year with apalike bibliography style.
First we need to modify the .bst file in a way to share the expected citation sort order with us. That can be done by adding
FUNCTION {sort.again}
{
year field.or.null sortify
" "
*
sort.label
*
'sort.key$ :=
}
ITERATE {sort.again}
SORT
EXECUTE {newline$}
FUNCTION {produce.sortlist}
{
"\sortentry{"
cite$
"}"
write$
newline$
}
ITERATE {produce.sortlist}
to the end of a renamed copy of the .bst file.
For plainnat.bst the modified plainnat-sortcites.bst would look like https://gist.github.com/moewew/783c43bd536de4198d5f5166c886fb61. The exact details can vary from style to style.
Then we need document-level code that can extract the sorting information from the .bbl file and use it for sorting. The following code is only slightly modified code from natbib.sty.
\documentclass[british]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage{csquotes}
\usepackage[sort]{natbib}
\bibliographystyle{plainnat-sortcites}
\usepackage{etoolbox}
\makeatletter
\newcommand{\sortentry}[1]{%
\if@filesw
\immediate\write@auxout{\string\scNAT@aux@sortentry{#1}}%
\fi}
\newcommand{\scNAT@aux@sortentry}{%
\listgadd{\scNAT@bibsortlist}}
\newcommand*{\scNAT@bibsortlist}{}
\newcommand*{\scNAT@citekeys}{}
\newcommand*{\scNAT@writetocitelistsort}[1]{%
\ifinlist{#1}{\scNAT@citekeys}
{\ifdefvoid{\NAT@cite@list}
{\def\NAT@cite@list{#1}}
{\expandafter\def\expandafter\NAT@cite@list\expandafter{\NAT@cite@list,#1}}%
\listgadd{\scNAT@foundkeys}{#1}}
{}}
% Let's be paranoid: we might have dropped an item that
% has not made it to the bibliography yet
\newcommand*{\scNAT@writetocitelistforgotten}[1]{%
\ifinlist{#1}{\scNAT@foundkeys}
{}
{\ifdefvoid{\NAT@cite@list}
{\def\NAT@cite@list{#1}}
{\expandafter\def\expandafter\NAT@cite@list\expandafter{\NAT@cite@list,#1}}}}
\newcommand*{\scNAT@sortcites}[1]{%
\let\NAT@cite@list@empty
\let\scNAT@citekeys@empty
\let\scNAT@foundkeys@empty
\forcsvlist{\listadd{\scNAT@citekeys}}{#1}%
\forlistloop{\scNAT@writetocitelistsort}{\scNAT@bibsortlist}%
\forlistloop{\scNAT@writetocitelistforgotten}{\scNAT@citekeys}%
}
\def\NAT@citex%
[#1][#2]#3{%
\NAT@reset@parser
\NAT@sort@cites{#3}%
\scNAT@sortcites{#3}%<- this is new
\NAT@reset@citea
@cite{\let\NAT@nm@empty\let\NAT@year@empty
@for@citeb:=\NAT@cite@list\do
{@safe@activestrue
\edef@citeb{\expandafter@firstofone@citeb@empty}%
@safe@activesfalse
@ifundefined{b@@citeb@extra@b@citeb}{@citea%
{\reset@font\bfseries ?}\NAT@citeundefined
\PackageWarning{natbib}%
{Citation `@citeb' on page \thepage \space undefined}\def\NAT@date{}}%
{\let\NAT@last@nm=\NAT@nm\let\NAT@last@yr=\NAT@year
\NAT@parse{@citeb}%
\ifNAT@longnames@ifundefined{bv@@citeb@extra@b@citeb}{%
\let\NAT@name=\NAT@all@names
\global@namedef{bv@@citeb@extra@b@citeb}{}}{}%
\fi
\ifNAT@full\let\NAT@nm\NAT@all@names\else
\let\NAT@nm\NAT@name\fi
\ifNAT@swa\ifcase\NAT@ctype
\if\relax\NAT@date\relax
@citea\NAT@hyper@{\NAT@nmfmt{\NAT@nm}\NAT@date}%
\else
\ifx\NAT@last@nm\NAT@nm\NAT@yrsep
\ifx\NAT@last@yr\NAT@year
\def\NAT@temp{{?}}%
\ifx\NAT@temp\NAT@exlab\PackageWarningNoLine{natbib}%
{Multiple citation on page \thepage: same authors and
year\MessageBreak without distinguishing extra
letter,\MessageBreak appears as question mark}\fi
\NAT@hyper@{\NAT@exlab}%
\else\unskip\NAT@spacechar
\NAT@hyper@{\NAT@date}%
\fi
\else
@citea\NAT@hyper@{%
\NAT@nmfmt{\NAT@nm}%
\hyper@natlinkbreak{%
\NAT@aysep\NAT@spacechar}{@citeb@extra@b@citeb
}%
\NAT@date
}%
\fi
\fi
\or@citea\NAT@hyper@{\NAT@nmfmt{\NAT@nm}}%
\or@citea\NAT@hyper@{\NAT@date}%
\or@citea\NAT@hyper@{\NAT@alias}%
\fi \NAT@def@citea
\else
\ifcase\NAT@ctype
\if\relax\NAT@date\relax
@citea\NAT@hyper@{\NAT@nmfmt{\NAT@nm}}%
\else
\ifx\NAT@last@nm\NAT@nm\NAT@yrsep
\ifx\NAT@last@yr\NAT@year
\def\NAT@temp{{?}}%
\ifx\NAT@temp\NAT@exlab\PackageWarningNoLine{natbib}%
{Multiple citation on page \thepage: same authors and
year\MessageBreak without distinguishing extra
letter,\MessageBreak appears as question mark}\fi
\NAT@hyper@{\NAT@exlab}%
\else
\unskip\NAT@spacechar
\NAT@hyper@{\NAT@date}%
\fi
\else
@citea\NAT@hyper@{%
\NAT@nmfmt{\NAT@nm}%
\hyper@natlinkbreak{\NAT@spacechar\NAT@@open\if#1\else#1\NAT@spacechar\fi}%
{@citeb@extra@b@citeb}%
\NAT@date
}%
\fi
\fi
\or@citea\NAT@hyper@{\NAT@nmfmt{\NAT@nm}}%
\or@citea\NAT@hyper@{\NAT@date}%
\or@citea\NAT@hyper@{\NAT@alias}%
\fi
\if\relax\NAT@date\relax
\NAT@def@citea
\else
\NAT@def@citea@close
\fi
\fi
}}\ifNAT@swa\else\if#2\else\NAT@cmt#2\fi
\if\relax\NAT@date\relax\else\NAT@@close\fi\fi}{#1}{#2}}
\makeatother
\begin{filecontents}{\jobname.bib}
@book{aardvark,
author = {Anne Aardvark},
title = {Ardvarking},
year = {2020},
}
@book{zebra,
author = {Zoe Zebra},
title = {Zebraing},
year = {2010},
}
\end{filecontents}
\begin{document}
\citep{zebra,aardvark}
\citep{aardvark,zebra}
\bibliography{\jobname}
\end{document}
