I think @dwaynecrooks covered the technical aspects of the question. But I believe your problem implies a design aspect too.
How to grow Elm code?
As others pointed out: thinking in terms of components will almost certainly take you down a not so appealing path with Elm. (Many people start there. I and my team started there over 2 years ago, and it took us 3 apps/major redesigns to get to a point where I think we can be happy at least about the fundamentals.)
Instead of components, I suggest you should think of your Elm application as a tree. Each node of your tree represents a level of abstraction, and describes the behavior of your application on that level. When you have the feeling that there is too much detail for a given level, you can start thinking about how new, lower levels of abstraction could be introduced as child nodes.
In practice each node is implemented in its own Elm module: parents import their children. You may also consider that you don't have to stick to the usual model/update/view
signatures, rather you should focus on the particularities of your app's domain. This is what – in my read – Richard Feldman is doing in his Real World SPA example app. And Evan's Life of a file talk is related to this question too.
The case of navbar
+ body
Regarding your particular case – it is not a rare case – here is my experience. If we say that our webapp has a navbar and then some body, this is a pretty static description of the app. This kind of description may fit a component based thinking, but it is less helpful if you want to end up with an elegant Elm app.
Instead, it is worth trying to describe the behavior of your app at this level of abstraction, which may sound something like this: The user can select x
,y
,z
items in a navbar. Clicking on those items will affect the item in q
way and also affect the body in either a
, or b
way. He can also click on v
in the navbar, which would show a popup or do w
, which logs him out of the app.
If you take this description and apply the logic that I described above, you should probably end up with some sort of a design where most of your navbar is described in your highest level of abstraction. This includes items x
, y
, z
, v
and behaviors a
, b
, w
. Now, behavior a
may mean that a specific, rich page must be displayed, which has its own detailed behavior that is described on a lower level of abstraction, whereas behavior b
may mean that based on the selection some content must be loaded, and again the details of this loading process is worked out on a lower level of abstraction. And so on.
When we started approaching the problem this way, it became much more straight forward to find out how to split up logic, and how to deal with special cases.
We realized, for instance, that when someone said that a page wants to display some stuff "in the navbar", what she really meant was that the navbar should collapse (or transform) for a particular page so that page can display it's own header in that area.
Focusing on the app's behavior instead of static content areas helped with this.