In practice, this isn't an issue. Component lifecycle hooks and methods (which is where anything involving raw DOM access or animation takes place) never run on the server — the SSR renderer just generates some HTML for a given initial state. Once you grok that, it's easy. Certainly much easier than maintaining two codebases in parallel!
Then you have a server/client difference if the lifecycle hooks change the dom (like a jQuery plugin or something). So you have to figure out what to do on the server.
With Ember and React at least, it's the same if you use 100% client-rendered elements; the component/template renders to the page, then a lifecycle hook is triggered, and then your custom code/plugin runs.
The client handles the dynamic aspect of time, whether that's event handlers that may be invoked later or animations which may run at some point and for some length of time.
The server only needs to handle it's default, but as other commenters mentioned, lifecycle hooks help manage when some logic gets run.
I think it does take some time to think universally, but it's not all confusion and hair pulling.
In fact, it kinda reminds me of writing testable code. You need to separate some concerns, but ultimately, it's a set of conventions that help you get there.
I can tell you, there are plenty of times when even a rule of two isn't worth the maintenance effort that's caused by rendering HTML in two different places.
If it doesn't make sense to render on server (last child tag for Google maps? YouTube? Calendar datetimepicker?) Then wrap it in a simple <no-ssr> tag that has existed for a long time in all major frameworks supporting SSR. Interesting problem, but when it ever rarely actually shows up, it has an obvious, practical, and proven solution.
Is it stranfe that I actually prefer two codebases? I find the hard split (or just client/server architecture) to provide clarity, focus and peace of mind.