There are two ways to approach this problem:
1 - Are your dependant components stand alone and reused everywhere
or
2 - Is this part of a large system that's co-dependant but well modularised into different projects.
I'm not 100% sure what the best practice would be in the first scenario but I can explain what I do in the second.
Say you have the following structure
/trunk
/trunk/Rob.Core
/trunk/Rob.MyCoolApplication.Server
/trunk/Ron.MyCoolApplication.Client
Each of those are "top level" applications, each with their own solution and targets, they compile independently but they're part of the same system.
In order to get "smooth" build integration with minimal fuss, I'd always recommend trying to reproduce the same environment on both dev and build servers. As such, when you check out to edit these applications, you should check out /trunk into /MyTrunkFolder.
Now, what I've tended to do, is as a post build action on any reusable .dll's or components, set up a post build action that xcopys it's build output (in release mode) to:
/trunk/CompiledOutputs/AppOrLibName/
It's important that this folder is never checked in, it should be generated by building the pre-requisite solutions.
Now, when adding references in Rob.MyCoolApplication.Server to Rob.Core, you actually add a reference to /trunk/CompiledOutputs/Rob.Core/some.dll.
This way your development environment looks exactly like your build server.
Once you have your dependencies wired up correctly, you then need to add triggers in your CI system. In CCNET, for the above example, I'd put a trigger on the successful build of Rob.Core to trigger the build of Rob.MyCoolApplication.Server.
When you do a fresh checkout of your system, it's advisable to have a batch script that triggers msbuild in dependency order to build your environment.
The above is only valid when you always want your applications to be build against the latest versions of your components. If that wasn't the case, you'd probably want to have a checked in folder of the referenced assemblies as part of the application that used them, and then manually update to new versions of your compiled components as and when it's valid to do so.
There's very little written on this subject so most of this was just felt out and extrapolated over 3-4 projects, but it's certainly been repeatable and reliable on my last two LARGE projects. As ever, obviously, your mileage may vary.