diff --git a/entries/static/entries/js/components/LexicalUnitEdit.js b/entries/static/entries/js/components/LexicalUnitEdit.js
index 5a95284d795aba1279fc05f1dbdbcde09142bb3b..cf16cb980c74ca11c4c754f7c525e38851d1e677 100644
--- a/entries/static/entries/js/components/LexicalUnitEdit.js
+++ b/entries/static/entries/js/components/LexicalUnitEdit.js
@@ -26,6 +26,9 @@ export default {
     }
   },
   methods: {
+    hasWhiteSpace(s) {
+      return /\s/g.test(s);
+    },
     async loadFrame() {
       try {
         var data = {'unified_frame_id': this.unifiedFrameId};
@@ -120,23 +123,28 @@ export default {
             if (v === 1) {
               e.preventDefault();
               var title = f.title;
-              var data = { 'unified_frame_id' : this.unified_frame.id, 'unified_frame_title' : title };
-              $.ajax({
-                type     : 'post',
-                url      : '/' + lang + '/unifier/save_unified_frame_title/',
-                dataType : 'json',
-                data     : data,
-                timeout  : 60000,
-                success  : function(response) {
-                  show_info('Tytuł ramy zosał zapisany');
-                  $.prompt.close();
-                  this.loadFrame();
-                }.bind(this),
-                error: function(request, errorType, errorMessage) {
-                  show_error(errorType + ' (' + errorMessage + ')');
-                  $.prompt.close();
-                }
-              });
+
+              if(this.hasWhiteSpace(title)) {
+                alert(gettext("Nazwa zunifikowanej ramy nie może zawierać białych znaków."));
+              } else {
+                var data = {'unified_frame_id': this.unified_frame.id, 'unified_frame_title': title};
+                $.ajax({
+                  type: 'post',
+                  url: '/' + lang + '/unifier/save_unified_frame_title/',
+                  dataType: 'json',
+                  data: data,
+                  timeout: 60000,
+                  success: function (response) {
+                    show_info('Tytuł ramy zosał zapisany');
+                    $.prompt.close();
+                    this.loadFrame();
+                  }.bind(this),
+                  error: function (request, errorType, errorMessage) {
+                    show_error(errorType + ' (' + errorMessage + ')');
+                    $.prompt.close();
+                  }
+                });
+              }
             }
           }.bind(this)
         }
@@ -327,6 +335,49 @@ export default {
           });
         }
       }
+    },
+    duplicate() {
+      let title = this.unified_frame.title != null ? this.unified_frame.title : '';
+      var duplicate_popup = {
+        state0: {
+          title: 'Podaj nazwÄ™ zduplikowanej ramy',
+          html: '<input type="text" size="32" value="KOPIA_' + title + '" name="title" />',
+          buttons: { Anuluj: 0, Zapisz: 1},
+          focus: -1,
+          submit: function(e,v,m,f){
+            if (v == 0) {
+              e.preventDefault();
+              $.prompt.close();
+            }
+            if (v === 1) {
+              e.preventDefault();
+              var title = f.title;
+
+              if(this.hasWhiteSpace(title)) {
+                alert(gettext("Nazwa zunifikowanej ramy nie może zawierać białych znaków."));
+              } else {
+                var data = {'unified_frame_id': this.unified_frame.id, 'target_unified_frame_title': title};
+                $.ajax({
+                  type: 'post',
+                  url: '/' + lang + '/unifier/duplicate_unified_frame/',
+                  dataType: 'json',
+                  data: data,
+                  timeout: 60000,
+                  success: function (response) {
+                    show_info('Zunifikowana rama została zduplikowana.');
+                    $.prompt.close();
+                  }.bind(this),
+                  error: function (request, errorType, errorMessage) {
+                    show_error(errorType + ' (' + errorMessage + ')');
+                    $.prompt.close();
+                  }
+                });
+              }
+            }
+          }.bind(this)
+        }
+      }
+      $.prompt(duplicate_popup);
     }
   },
   mounted () {
@@ -356,7 +407,7 @@ export default {
           <td id="change-role" @click="changeRole" style="padding: 10px 15px 10px 15px; color: #000000;">Zmień rolę</td>
           <td id="remove-arg" @click="removeArgument" style="padding: 10px 15px 10px 15px; color: #000000;">Usuń argum.</td>
           <td id="change-windows" style="padding: 10px 15px 10px 15px; color: #000000;">Zamień okna</td>
-          <td id="duplicates" style="padding: 10px 15px 10px 15px; color: #000000;">Duplikuj</td>
+          <td id="duplicates" @click="duplicate" style="padding: 10px 15px 10px 15px; color: #000000;">Duplikuj</td>
           <td id="save-changes" style="padding: 10px 15px 10px 15px; color: #000000;">Zapisz</td>
         </tr>
       </table>
diff --git a/entries/static/entries/js/unification.js b/entries/static/entries/js/unification.js
index e3e296b3c91877bdcc5ac63eb20dc42d84e508f1..34df25b35e80cc4344e110a6e13d5c26566ef055 100644
--- a/entries/static/entries/js/unification.js
+++ b/entries/static/entries/js/unification.js
@@ -386,11 +386,12 @@ function show_unified_frame_lexical_units(frames) {
     $('#unified-frame-lexical-units').append($(frames2lexical_unitsHTML(frames)));
 }
 
-function extract_frames_to_new_frame(unified_frame_id, slowal_frame_ids, new_unified_frame_id) {
+function extract_frames_to_new_frame(unified_frame_id, slowal_frame_ids, target_unified_frame_id) {
     check_import_status();
     clear_entry();
     show_entry_spinners();
-    var data = { 'unified_frame_id' : unified_frame_id, 'slowal_frame_ids' : JSON.stringify(slowal_frame_ids), 'new_unified_frame_id': new_unified_frame_id };
+    var data = { 'unified_frame_id' : unified_frame_id, 'slowal_frame_ids' : JSON.stringify(slowal_frame_ids),
+        'target_unified_frame_id': target_unified_frame_id };
     $.ajax({
         type     : 'post',
         url      : '/' + lang + '/unifier/extract_frames_to_new_frame/',
diff --git a/unifier/models.py b/unifier/models.py
index e804f174406433c6994b0346755e63e106176b3b..5b0c636fb2e55d755044eab1ff11e367a8c814a9 100644
--- a/unifier/models.py
+++ b/unifier/models.py
@@ -37,20 +37,18 @@ class UnifiedFrame(models.Model):
     ) -> "UnifiedFrame":
         new_unified_frame_arguments = None
         if not new_frame:
-            new_frame = UnifiedFrame.objects.create(title='[Kopia] '+self.title if self.title is not None else '[Kopia]')
+            new_frame = UnifiedFrame.objects.create()
             new_frame.save()
             unified_frame_arguments = UnifiedFrameArgument.objects.filter(unified_frame=self)
-            cnt = UnifiedFrameArgument.objects.count()+1
             old_2_new_argument_mapping = {}
             for unified_frame_argument in unified_frame_arguments:
-                new_unified_frame_argument = UnifiedFrameArgument.objects.create(pk=cnt,
+                new_unified_frame_argument = UnifiedFrameArgument.objects.create(
                                                                   role_type=unified_frame_argument.role_type,
                                                                   role=unified_frame_argument.role,
                                                                   unified_frame=new_frame)
                 new_unified_frame_argument.proposed_roles.set(unified_frame_argument.proposed_roles.all())
                 new_unified_frame_argument.save()
                 old_2_new_argument_mapping[unified_frame_argument.id] = new_unified_frame_argument.id
-                cnt = cnt + 1
         else:
             new_unified_frame_arguments = UnifiedFrameArgument.objects.filter(unified_frame=new_frame).all()
             unified_frame_arguments = UnifiedFrameArgument.objects.filter(unified_frame=self).all()
@@ -77,6 +75,23 @@ class UnifiedFrame(models.Model):
 
         return new_frame
 
+    @transaction.atomic
+    def duplicate(
+            self, new_frame_title: Optional = None
+    ) -> "UnifiedFrame":
+        new_frame = UnifiedFrame.objects.create(title=new_frame_title)
+        new_frame.save()
+        unified_frame_arguments = UnifiedFrameArgument.objects.filter(unified_frame=self)
+        for unified_frame_argument in unified_frame_arguments:
+            new_unified_frame_argument = UnifiedFrameArgument.objects.create(
+                role_type=unified_frame_argument.role_type,
+                role=unified_frame_argument.role,
+                unified_frame=new_frame)
+            new_unified_frame_argument.proposed_roles.set(unified_frame_argument.proposed_roles.all())
+            new_unified_frame_argument.save()
+
+        return new_frame
+
 
 class UnifiedFrameArgument(models.Model):
     role_type = models.ForeignKey(RoleType, on_delete=models.PROTECT, default=None, blank=True, null=True)
diff --git a/unifier/urls.py b/unifier/urls.py
index ee3a35c1165dcc690afd7f77159bb513a54eaa2c..c8816554d2da9bbf7ed58ff74cc127a1be06c12c 100644
--- a/unifier/urls.py
+++ b/unifier/urls.py
@@ -18,5 +18,6 @@ urlpatterns = [
     path('save_new_role/', views.save_new_role, name='save_new_role'),
     path('add_argument/', views.add_argument, name='add_argument'),
     path('remove_argument/', views.remove_argument, name='remove_argument'),
+    path('duplicate_unified_frame/', views.duplicate_unified_frame, name='duplicate_unified_frame'),
 
 ]
diff --git a/unifier/views.py b/unifier/views.py
index 204c2d197aded3780e20802c6f9455604b9a2737..d2302dad92702356024c65e5a2bab139d04f78e3 100644
--- a/unifier/views.py
+++ b/unifier/views.py
@@ -60,24 +60,28 @@ def get_unified_frames(request):
     if request.method == 'POST':
         scroller_params = get_scroller_params(request.POST)
 
+        res = {}
+        unifiedFrames = UnifiedFrame.objects.all();
+        for unifiedFrame in unifiedFrames:
+            res[unifiedFrame.id] = {
+                'id': str(unifiedFrame.id),
+                'title': unifiedFrame.title,
+                'status': unifiedFrame.status,
+                'assignee_username': None,
+            }
+
         unifiedFrame2SlowalFrameMappings = UnifiedFrame2SlowalFrameMapping.objects.all();
 
-        res = {}
         for mapping in unifiedFrame2SlowalFrameMappings:
-            res[mapping.unified_frame.id] = res.get(mapping.unified_frame.id, [])
-            res[mapping.unified_frame.id].append(mapping)
+            unifiedFrame = res[mapping.unified_frame.id]
+            title = unifiedFrame['title'] if unifiedFrame['title'] is not None else '['+mapping.slowal_frame.lexical_units.first().base+']'
+            unifiedFrame['title'] = title
+            if unifiedFrame['assignee_username'] is None:
+                unifiedFrame['assignee_username'] = assignment.user.username if (assignment := mapping.slowal_frame.assignments.first()) else None
 
         resProcessed = []
         for key, value in res.items():
-            title = value[0].unified_frame.title if value[0].unified_frame.title is not None else '['+value[0].slowal_frame.lexical_units.first().base+']'
-            resProcessed.append({
-                'id': str(value[0].unified_frame.id),
-                'title': title,
-                'status': value[0].unified_frame.status,
-                'assignee_username': (
-                    assignment.user.username if (assignment := value[0].slowal_frame.assignments.first()) else None
-                ),
-            })
+            resProcessed.append(value)
 
         ret = {
                 'draw' : scroller_params['draw'],
@@ -212,17 +216,29 @@ def get_unified_frame(request):
 def extract_frames_to_new_frame(request):
     if request.method == 'POST':
         unified_frame_id = request.POST['unified_frame_id']
-        new_unified_frame_id = request.POST['new_unified_frame_id']
+        target_unified_frame_id = request.POST['target_unified_frame_id']
         slowal_frame_ids = json.loads(request.POST['slowal_frame_ids'])
+
         unified_frame = UnifiedFrame.objects.get(id=unified_frame_id)
         slowal_frames = Frame.objects.filter(id__in=slowal_frame_ids)
         new_unified_frame = None
-        if new_unified_frame_id != '':
-            new_unified_frame = UnifiedFrame.objects.get(id=new_unified_frame_id)
+        if target_unified_frame_id != '':
+            new_unified_frame = UnifiedFrame.objects.get(id=target_unified_frame_id)
         new_frame_fullfiled_and_saved = unified_frame.extract_frames_to(slowal_frames=slowal_frames, new_frame=new_unified_frame)
         return JsonResponse(get_unified_frame_json(new_frame_fullfiled_and_saved, request))
     return JsonResponse({})
 
+@ajax_required
+def duplicate_unified_frame(request):
+    if request.method == 'POST':
+        unified_frame_id = request.POST['unified_frame_id']
+        target_unified_frame_title = request.POST['target_unified_frame_title']
+
+        unified_frame = UnifiedFrame.objects.get(id=unified_frame_id)
+        new_frame = unified_frame.duplicate(new_frame_title=target_unified_frame_title)
+        return JsonResponse(get_unified_frame_json(new_frame, request))
+    return JsonResponse({})
+
 @ajax_required
 def change_slowal2unified_fram_argument_mapping(request):
     if request.method == 'POST':