For the most part, LAPACK represents unitary matrices like $\mathbf U$ as products of householder reflectors, and provides specialized routines to work with that representation (for instance, use [dormqr] to multiply by $\mathbf U$, or [dorgqr] to explicitly tabulate $\mathbf U$ into a dense matrix).
If you already have $\mathbf U$ in householder format, it would be more idiomatic to use [dormqr] to update A, instead of [dgemm]. In particular, two calls to [dormqr] can update $\mathbf B = \mathbf U^T \mathbf A \mathbf U$ in place (overwriting $\mathbf A$ with $\mathbf B$), while [dgemm] will require a temporary matrix. This point might be somewhat academic, though, as [dormqr] requires additional workspace while [dgemm] does not. Although there is a workspace-free routine [dorm2r] that performs a similar function as [dormqr], it's not recommended because it's a BLAS2 algorithm and won't be as fast.
I suppose if I already was just given $\mathbf U$ explicitly tabulated, I'd probably just stick with [dgemm]. But if you have control over how $\mathbf U$ is generated/computed (for instance, some hand-rolled gram-schmidt procedure applied to some other basis set), I'd strongly consider refactoring everything to use the LAPACK tooling instead (that is use [dgeqrf] to orthogonalize your basis into $\mathbf U$ and then use [dormqr] to apply it, there might not be any need to ever form it explicitly).
Out of curiousity, what do you do with $\mathbf B$ once you have it? There might be more refactoring/optimization opportunities if you can share more about the larger process.