Solution 1:

Actually <template> elements can be imported from another document via HTML Imports, along with the Javascript code that will define the custom element:

<link rel="import" src="my-custom-element.html">
...
<custom-element></custom-element>

So it doesn't need to be included in a every HTML document. This post shows a minimal example.

HTML Imports are implemented only in Chrome and Opera. If you want to use them in the with Firefox and Safari you'll need to use the HTML Imports polyfill.

On the other hand and for the moment, Mozilla and Apple don't intend to implement HTML Imports natively in their respective browsers. Therefore they recommend to define custom elements with pure Javascript modules (with import or <script src="...">), and promote template literals strings instead, which offer some advantages (variables, functions), but are sometimes more complicated to code in an IDE (because of their string representation).

Maybe in the future standard HTML modules will be adopted by all browsers, and <template> will come back in the spotlight...

Note that without HTML Imports you can still import yourself some HTML documents with fetch():

fetch( "template.html" )
    .then( stream => stream.text() )
    .then( text => 
        customElements.define( "c-e", class extends HTMLElement {
            constructor() {
                super()
                this.attachShadow( { mode: 'open'} )
                    .innerHTML = text
            }
        } )
    )

Update 2019

HTML Imports won't be supported natively after Chrome 73. You should then use the other solutions listed above (the polyfill, an alternate module loader, JS import, or a direct download with fetch).