diff --git a/src/Models/Behaviors/HasFiles.php b/src/Models/Behaviors/HasFiles.php
index 160c34735e..d7e79aebed 100644
--- a/src/Models/Behaviors/HasFiles.php
+++ b/src/Models/Behaviors/HasFiles.php
@@ -18,7 +18,7 @@ public function files()
             File::class,
             'fileable',
             config('twill.fileables_table', 'twill_fileables')
-        )->withPivot(['role', 'locale'])
+        )->withPivot(['id', 'role', 'locale'])
             ->withTimestamps()->orderBy(config('twill.fileables_table', 'twill_fileables') . '.id', 'asc');
     }
 
diff --git a/src/Models/Behaviors/HasMedias.php b/src/Models/Behaviors/HasMedias.php
index 685d14305f..b3a54eaacb 100644
--- a/src/Models/Behaviors/HasMedias.php
+++ b/src/Models/Behaviors/HasMedias.php
@@ -45,6 +45,7 @@ public function medias()
             'mediable',
             config('twill.mediables_table', 'twill_mediables')
         )->withPivot([
+            'id',
             'crop',
             'role',
             'crop_w',
diff --git a/src/Repositories/Behaviors/HandleFiles.php b/src/Repositories/Behaviors/HandleFiles.php
index 876b7fa0cf..766482ab06 100644
--- a/src/Repositories/Behaviors/HandleFiles.php
+++ b/src/Repositories/Behaviors/HandleFiles.php
@@ -2,9 +2,10 @@
 
 namespace A17\Twill\Repositories\Behaviors;
 
+use A17\Twill\Facades\TwillUtil;
+use A17\Twill\Models\Behaviors\HasFiles;
 use A17\Twill\Models\Contracts\TwillModelContract;
 use A17\Twill\Models\File;
-use Illuminate\Support\Arr;
 use Illuminate\Support\Str;
 use Illuminate\Support\Collection;
 
@@ -37,7 +38,7 @@ public function hydrateHandleFiles($object, $fields)
     }
 
     /**
-     * @param \A17\Twill\Models\Model $object
+     * @param \A17\Twill\Models\Model|HasFiles $object
      * @param array $fields
      * @return void
      */
@@ -47,7 +48,7 @@ public function afterSaveHandleFiles($object, $fields)
             return;
         }
 
-        $object->files()->sync($this->getFiles($fields));
+        TwillUtil::syncUsingPrimaryKey($object->files(), $this->getFiles($fields));
     }
 
     /**
@@ -73,11 +74,11 @@ private function getFiles($fields)
                     || in_array($role, config('twill.block_editor.files', []))
                 ) {
                     Collection::make($filesForRole)->each(function ($file) use (&$files, $role, $locale) {
-                        $files->push([
+                        $files[$file['pivot_id'] ?? uniqid('file')] = [
                             'file_id' => $file['id'],
                             'role' => $role,
                             'locale' => $locale,
-                        ]);
+                        ];
                     });
                 }
             }
@@ -97,8 +98,8 @@ public function getFormFieldsHandleFiles($object, $fields)
         if ($object->has('files')) {
             foreach ($object->files->groupBy('pivot.role') as $role => $filesByRole) {
                 foreach ($filesByRole->groupBy('pivot.locale') as $locale => $filesByLocale) {
-                    $fields['files'][$locale][$role] = $filesByLocale->map(function ($file) {
-                        return $file->toCmsArray();
+                    $fields['files'][$locale][$role] = $filesByLocale->map(function (File $file) {
+                        return $file->toCmsArray() + ['pivot_id' => $file->pivot->id];
                     });
                 }
             }
@@ -107,9 +108,13 @@ public function getFormFieldsHandleFiles($object, $fields)
         return $fields;
     }
 
+    /**
+     * @param HasFiles|TwillModelContract $object
+     * @param HasFiles|TwillModelContract $newObject
+     */
     public function afterDuplicateHandleFiles(TwillModelContract $object, TwillModelContract $newObject): void
     {
-        $newObject->files()->sync(
+        $newObject->files()->attach(
             $object->files->mapWithKeys(function ($file) use ($object) {
                 return [
                     $file->id => Collection::make($object->files()->getPivotColumns())->mapWithKeys(
diff --git a/src/Repositories/Behaviors/HandleMedias.php b/src/Repositories/Behaviors/HandleMedias.php
index ed9017ebc2..729a72f42c 100644
--- a/src/Repositories/Behaviors/HandleMedias.php
+++ b/src/Repositories/Behaviors/HandleMedias.php
@@ -3,6 +3,8 @@
 namespace A17\Twill\Repositories\Behaviors;
 
 use A17\Twill\Facades\TwillBlocks;
+use A17\Twill\Facades\TwillUtil;
+use A17\Twill\Models\Behaviors\HasMedias;
 use A17\Twill\Models\Contracts\TwillModelContract;
 use A17\Twill\Models\Media;
 use Illuminate\Support\Arr;
@@ -43,7 +45,7 @@ public function hydrateHandleMedias($object, $fields)
     }
 
     /**
-     * @param \A17\Twill\Models\Model $object
+     * @param \A17\Twill\Models\Model|HasMedias $object
      * @param array $fields
      * @return void
      */
@@ -53,7 +55,7 @@ public function afterSaveHandleMedias($object, $fields)
             return;
         }
 
-        $object->medias()->sync($this->getMedias($fields));
+        TwillUtil::syncUsingPrimaryKey($object->medias(), $this->getMedias($fields));
     }
 
     /**
@@ -80,7 +82,7 @@ private function getMedias($fields)
                         $customMetadatas = $media['metadatas']['custom'] ?? [];
                         if (isset($media['crops']) && !empty($media['crops'])) {
                             foreach ($media['crops'] as $cropName => $cropData) {
-                                $medias->push([
+                                $medias[$cropData['pivot_id'] ?? uniqid('media')] = [
                                     'media_id' => $media['id'],
                                     'crop' => $cropName,
                                     'role' => $role,
@@ -91,11 +93,11 @@ private function getMedias($fields)
                                     'crop_x' => $cropData['x'],
                                     'crop_y' => $cropData['y'],
                                     'metadatas' => json_encode($customMetadatas),
-                                ]);
+                                ];
                             }
                         } else {
                             foreach ($this->getCrops($role) as $cropName => $cropDefinitions) {
-                                $medias->push([
+                                $medias[$media['pivot_id'] ?? uniqid('media')] = [
                                     'media_id' => $media['id'],
                                     'crop' => $cropName,
                                     'role' => $role,
@@ -106,7 +108,7 @@ private function getMedias($fields)
                                     'crop_x' => null,
                                     'crop_y' => null,
                                     'metadatas' => json_encode($customMetadatas),
-                                ]);
+                                ];
                             }
                         }
                     });
@@ -157,12 +159,14 @@ private function getMediaFormItems($medias)
             $item = $mediasById->first();
 
             $itemForForm = $item->toCmsArray();
+            $itemForForm['pivot_id'] = $item->pivot->id;
 
             $itemForForm['metadatas']['custom'] = json_decode($item->pivot->metadatas, true);
 
             foreach ($mediasById->groupBy('pivot.crop') as $crop => $mediaByCrop) {
                 $media = $mediaByCrop->first();
                 $itemForForm['crops'][$crop] = [
+                    'pivot_id' => $media->pivot->id,
                     'name' => $media->pivot->ratio,
                     'width' => $media->pivot->crop_w,
                     'height' => $media->pivot->crop_h,
diff --git a/src/Repositories/BlockRepository.php b/src/Repositories/BlockRepository.php
index 06e8e2b835..ed721d7f7c 100644
--- a/src/Repositories/BlockRepository.php
+++ b/src/Repositories/BlockRepository.php
@@ -3,6 +3,7 @@
 namespace A17\Twill\Repositories;
 
 use A17\Twill\Facades\TwillBlocks;
+use A17\Twill\Models\Block;
 use A17\Twill\Models\Contracts\TwillModelContract;
 use A17\Twill\Repositories\Behaviors\HandleFiles;
 use A17\Twill\Repositories\Behaviors\HandleMedias;
@@ -75,10 +76,11 @@ public function afterSave(TwillModelContract $model, array $fields): void
         parent::afterSave($model, $fields);
     }
 
+    /** @param Block $object */
     public function afterDelete(TwillModelContract $object): void
     {
-        $object->medias()->sync([]);
-        $object->files()->sync([]);
+        $object->medias()->detach();
+        $object->files()->detach();
 
         if (Schema::hasTable(config('twill.related_table', 'twill_related'))) {
             $object->clearAllRelated();
diff --git a/src/TwillUtil.php b/src/TwillUtil.php
index cb93efe352..7cd1ee582e 100644
--- a/src/TwillUtil.php
+++ b/src/TwillUtil.php
@@ -3,6 +3,7 @@
 namespace A17\Twill;
 
 use A17\Twill\Models\Contracts\TwillLinkableModel;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 use Illuminate\Database\Eloquent\Relations\Relation;
 use Illuminate\Support\Facades\Session;
 
@@ -103,4 +104,55 @@ private function pushToTempStore(string $key, int $frontendId, int $dbId): void
 
         Session::put(self::SESSION_FIELD, $sessionData);
     }
+
+    public function syncUsingPrimaryKey(BelongsToMany $relation, $ids, $detaching = true): array
+    {
+        return (function ($ids, $detaching = true) {
+            $changes = [
+                'attached' => [], 'detached' => [], 'updated' => [],
+            ];
+
+            // First we need to attach any of the associated models that are not currently
+            // in this joining table. We'll spin through the given IDs, checking to see
+            // if they exist in the array of current ones, and if not we will insert.
+            $current = $this->getCurrentlyAttachedPivots()
+                ->pluck('id')->all();
+
+            $records = $this->formatRecordsList($this->parseIds($ids));
+
+            // Next, we will take the differences of the currents and given IDs and detach
+            // all of the entities that exist in the "current" array but are not in the
+            // array of the new IDs given to the method which will complete the sync.
+            if ($detaching) {
+                $detach = array_diff($current, array_keys($records));
+
+                if (count($detach) > 0) {
+                    $this->newPivotQuery()->whereIn('id', $detach)->delete();
+
+                    $changes['detached'] = $this->castKeys($detach);
+                }
+            }
+
+            // Now we are finally ready to attach the new records. Note that we'll disable
+            // touching until after the entire operation is complete so we don't fire a
+            // ton of touch operations until we are totally done syncing the records.
+            $changes = array_merge(
+                $changes,
+                $this->attachNew($records, $current, false)
+            );
+
+            // Once we have finished attaching or detaching the records, we will see if we
+            // have done any attaching or detaching, and if we have we will touch these
+            // relationships if they are configured to touch on any database updates.
+            if (
+                count($changes['attached']) ||
+                count($changes['updated']) ||
+                count($changes['detached'])
+            ) {
+                $this->touchIfTouching();
+            }
+
+            return $changes;
+        })->call($relation, $ids, $detaching);
+    }
 }