diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp
index dfc3ab665e25f2f7e02cdb8396eb8937160e82ea..53662724fa08eb1f6352079fe17a74aae751d8d8 100644
--- a/src/plugins/qmljseditor/qmlcodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp
@@ -92,6 +92,68 @@ static QIcon iconForColor(const QColor &color)
     return pix;
 }
 
+
+static QString qualifiedNameId(AST::UiQualifiedId *it)
+{
+    QString text;
+
+    for (; it; it = it->next) {
+        if (! it->name)
+            continue;
+
+        text += it->name->asString();
+
+        if (it->next)
+            text += QLatin1Char('.');
+    }
+
+    return text;
+}
+
+static Interpreter::ObjectValue *newComponent(Interpreter::Engine *engine, const QString &name,
+                                              const QHash<QString, Document::Ptr> &userComponents,
+                                              QSet<QString> *processed)
+{
+    if (Interpreter::ObjectValue *object = engine->newQmlObject(name))
+        return object;
+
+    else if (! processed->contains(name)) {
+        processed->insert(name);
+
+        if (Document::Ptr doc = userComponents.value(name)) {
+            if (AST::UiProgram *program = doc->qmlProgram()) {
+                if (program->members) {
+                    if (AST::UiObjectDefinition *def = AST::cast<AST::UiObjectDefinition *>(program->members->member)) {
+                        const QString component = qualifiedNameId(def->qualifiedTypeNameId);
+                        Interpreter::ObjectValue *object = newComponent(engine, component, userComponents, processed);
+                        if (def->initializer) {
+                            for (AST::UiObjectMemberList *it = def->initializer->members; it; it = it->next) {
+                                if (AST::UiPublicMember *prop = AST::cast<AST::UiPublicMember *>(it->member)) {
+                                    if (prop->name && prop->memberType) {
+                                        //qDebug() << "add property:" << prop->name->asString();
+                                        object->setProperty(prop->name->asString(), engine->undefinedValue());
+                                    }
+                                }
+                            }
+                        }
+                        return object;
+                    }
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static Interpreter::ObjectValue *newComponent(Interpreter::Engine *engine,
+                                              const QString &name,
+                                              const QHash<QString, Document::Ptr> &userComponents)
+{
+    QSet<QString> processed;
+    return newComponent(engine, name, userComponents, &processed);
+}
+
 namespace {
 
 class ExpressionUnderCursor
@@ -622,23 +684,6 @@ bool QmlCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
     return false;
 }
 
-static QString qualifiedNameId(AST::UiQualifiedId *it)
-{
-    QString text;
-
-    for (; it; it = it->next) {
-        if (! it->name)
-            continue;
-
-        text += it->name->asString();
-
-        if (it->next)
-            text += QLatin1Char('.');
-    }
-
-    return text;
-}
-
 int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 {
     m_editor = editor;
@@ -669,6 +714,8 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 
     Interpreter::Engine interp;
 
+    QHash<QString, Document::Ptr> userComponents;
+
     foreach (Document::Ptr doc, snapshot) {
         const QFileInfo fileInfo(doc->fileName());
 
@@ -681,6 +728,17 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         if (typeName.isEmpty())
             continue;
 
+        userComponents.insert(typeName, doc);
+    }
+
+    foreach (Document::Ptr doc, snapshot) {
+        const QFileInfo fileInfo(doc->fileName());
+
+        if (fileInfo.suffix() != QLatin1String("qml"))
+            continue;
+        else if (fileInfo.absolutePath() != currentFilePath) // ### FIXME include `imported' components
+            continue;
+
         QMapIterator<QString, IdSymbol *> it(doc->ids());
         while (it.hasNext()) {
             it.next();
@@ -691,8 +749,9 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
                 if (symbol->parentNode()) {
                     const QString component = symbol->parentNode()->name();
 
-                    if (const Interpreter::ObjectValue *object = interp.newQmlObject(component))
+                    if (const Interpreter::ObjectValue *object = newComponent(&interp, component, userComponents)) {
                         interp.globalObject()->setProperty(id, object);
+                    }
                 }
             }
         }
@@ -741,7 +800,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     else if (AST::UiObjectBinding *binding = AST::cast<AST::UiObjectBinding *>(declaringMember))
         declaringItemName = qualifiedNameId(binding->qualifiedTypeNameId);
 
-    Interpreter::ObjectValue *declaringItem = interp.newQmlObject(declaringItemName);
+    Interpreter::ObjectValue *declaringItem = newComponent(&interp, declaringItemName, userComponents);
     if (! declaringItem)
         declaringItem = interp.newQmlObject(QLatin1String("Item"));
 
@@ -758,7 +817,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     else if (AST::UiObjectBinding *binding = AST::cast<AST::UiObjectBinding *>(parentMember))
         parentItemName = qualifiedNameId(binding->qualifiedTypeNameId);
 
-    Interpreter::ObjectValue *parentItem = interp.newQmlObject(declaringItemName);
+    Interpreter::ObjectValue *parentItem = newComponent(&interp, declaringItemName, userComponents);
     if (! parentItem)
         parentItem = interp.newQmlObject(QLatin1String("Item"));