diff --git a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
index 3015f4400..12d621579 100644
--- a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
+++ b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
@@ -935,6 +935,30 @@ internal void InsertSense(ILexEntry lexEntry, ILexSense lexSense, BetweenPositio
         lexEntry.SensesOS.Add(lexSense);
     }
 
+    internal void InsertExampleSentence(ILexSense lexSense, ILexExampleSentence lexExample, BetweenPosition? between = null)
+    {
+        var previousExampleId = between?.Previous;
+        var nextExampleId = between?.Next;
+
+        var previousExample = previousExampleId.HasValue ? lexSense.ExamplesOS.FirstOrDefault(s => s.Guid == previousExampleId) : null;
+        if (previousExample is not null)
+        {
+            var insertI = lexSense.ExamplesOS.IndexOf(previousExample) + 1;
+            lexSense.ExamplesOS.Insert(insertI, lexExample);
+            return;
+        }
+
+        var nextExample = nextExampleId.HasValue ? lexSense.ExamplesOS.FirstOrDefault(s => s.Guid == nextExampleId) : null;
+        if (nextExample is not null)
+        {
+            var insertI = lexSense.ExamplesOS.IndexOf(nextExample);
+            lexSense.ExamplesOS.Insert(insertI, lexExample);
+            return;
+        }
+
+        lexSense.ExamplesOS.Add(lexExample);
+    }
+
     private void ApplySenseToLexSense(Sense sense, ILexSense lexSense)
     {
         if (lexSense.MorphoSyntaxAnalysisRA.GetPartOfSpeech()?.Guid != sense.PartOfSpeechId)
@@ -1067,9 +1091,10 @@ public Task DeleteSense(Guid entryId, Guid senseId)
         return Task.FromResult(lcmExampleSentence is null ? null : FromLexExampleSentence(senseId, lcmExampleSentence));
     }
 
-    internal void CreateExampleSentence(ILexSense lexSense, ExampleSentence exampleSentence)
+    internal void CreateExampleSentence(ILexSense lexSense, ExampleSentence exampleSentence, BetweenPosition? between = null)
     {
-        var lexExampleSentence = LexExampleSentenceFactory.Create(exampleSentence.Id, lexSense);
+        var lexExampleSentence = LexExampleSentenceFactory.Create(exampleSentence.Id);
+        InsertExampleSentence(lexSense, lexExampleSentence, between);
         UpdateLcmMultiString(lexExampleSentence.Example, exampleSentence.Sentence);
         var freeTranslationType = CmPossibilityRepository.GetObject(CmPossibilityTags.kguidTranFreeTranslation);
         var translation = CmTranslationFactory.Create(lexExampleSentence, freeTranslationType);
@@ -1078,7 +1103,7 @@ internal void CreateExampleSentence(ILexSense lexSense, ExampleSentence exampleS
             lexExampleSentence.Reference.get_WritingSystem(0));
     }
 
-    public async Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence)
+    public async Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence, BetweenPosition? between = null)
     {
         if (exampleSentence.Id == default) exampleSentence.Id = Guid.NewGuid();
         if (!SenseRepository.TryGetObject(senseId, out var lexSense))
@@ -1087,7 +1112,7 @@ public async Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid sens
         UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Example Sentence",
             "Remove example sentence",
             Cache.ServiceLocator.ActionHandler,
-            () => CreateExampleSentence(lexSense, exampleSentence));
+            () => CreateExampleSentence(lexSense, exampleSentence, between));
         return FromLexExampleSentence(senseId, ExampleSentenceRepository.GetObject(exampleSentence.Id));
     }
 
@@ -1124,6 +1149,28 @@ await Cache.DoUsingNewOrCurrentUOW("Update Example Sentence",
         return await GetExampleSentence(entryId, senseId, after.Id) ?? throw new NullReferenceException("unable to find example sentence with id " + after.Id);
     }
 
+    public Task MoveExampleSentence(Guid entryId, Guid senseId, Guid exampleSentenceId, BetweenPosition between)
+    {
+        if (!EntriesRepository.TryGetObject(entryId, out var lexEntry))
+            throw new InvalidOperationException("Entry not found");
+        if (!SenseRepository.TryGetObject(senseId, out var lexSense))
+            throw new InvalidOperationException("Sense not found");
+        if (!ExampleSentenceRepository.TryGetObject(exampleSentenceId, out var lexExample))
+            throw new InvalidOperationException("Example sentence not found");
+
+        ValidateOwnership(lexExample, entryId, senseId);
+
+        UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Move Example sentence",
+            "Move Example sentence back",
+            Cache.ServiceLocator.ActionHandler,
+            () =>
+            {
+                // LibLCM treats an insert as a move if the example sentence is already on the sense
+                InsertExampleSentence(lexSense, lexExample, between);
+            });
+        return Task.CompletedTask;
+    }
+
     public Task DeleteExampleSentence(Guid entryId, Guid senseId, Guid exampleSentenceId)
     {
         var lexExampleSentence = ExampleSentenceRepository.GetObject(exampleSentenceId);
diff --git a/backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSenseProxy.cs b/backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSenseProxy.cs
index ea4703301..19e14b121 100644
--- a/backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSenseProxy.cs
+++ b/backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSenseProxy.cs
@@ -113,15 +113,9 @@ public override required Guid Id
         }
     }
 
-    public override IList<ExampleSentence> ExampleSentences
+    public override List<ExampleSentence> ExampleSentences
     {
-        get =>
-            new UpdateListProxy<ExampleSentence>(
-                sentence => lexboxLcmApi.CreateExampleSentence(sense, sentence),
-                sentence => lexboxLcmApi.DeleteExampleSentence(sense.Owner.Guid, Id, sentence.Id),
-                i => new UpdateExampleSentenceProxy(sense.ExamplesOS[i], lexboxLcmApi),
-                sense.ExamplesOS.Count
-            );
+        get => throw new NotImplementedException();
         set => throw new NotImplementedException();
     }
 }
diff --git a/backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs b/backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs
index fcdfe555d..7d29a8a27 100644
--- a/backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs
+++ b/backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs
@@ -43,7 +43,9 @@ public async Task CanSyncRandomEntries()
         var actual = await _fixture.CrdtApi.GetEntry(after.Id);
         actual.Should().NotBeNull();
         actual.Should().BeEquivalentTo(after, options => options
-            .For(e => e.Senses).Exclude(s => s.Order));
+            .For(e => e.Senses).Exclude(s => s.Order)
+            .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
+            );
     }
 
     [Fact]
diff --git a/backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs b/backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
index e8100a5b8..c841dd42d 100644
--- a/backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
+++ b/backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
@@ -53,7 +53,8 @@ private void ShouldAllBeEquivalentTo(Dictionary<Guid, Entry> crdtEntries, Dictio
                     options => options
                         .For(e => e.Components).Exclude(c => c.Id)
                         .For(e => e.ComplexForms).Exclude(c => c.Id)
-                        .For(e => e.Senses).Exclude(s => s.Order),
+                        .For(e => e.Senses).Exclude(s => s.Order)
+                        .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order),
                     $"CRDT entry {crdtEntry.Id} was synced with FwData");
             }
         }
diff --git a/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs b/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
index 865a16988..f670bcb30 100644
--- a/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
+++ b/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
@@ -86,6 +86,7 @@ public async Task FirstSyncJustDoesAnImport()
         crdtEntries.Should().BeEquivalentTo(fwdataEntries,
             options => options
                 .For(e => e.Senses).Exclude(s => s.Order)
+                .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
                 .For(e => e.Components).Exclude(c => c.Id)
                 .For(e => e.ComplexForms).Exclude(c => c.Id));
     }
@@ -153,6 +154,7 @@ await crdtApi.CreateEntry(new Entry()
         crdtEntries.Should().BeEquivalentTo(fwdataEntries,
             options => options
                 .For(e => e.Senses).Exclude(s => s.Order)
+                .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
                 .For(e => e.Components).Exclude(c => c.Id)
                 .For(e => e.ComplexForms).Exclude(c => c.Id));
     }
@@ -235,6 +237,7 @@ public async Task CreatingAComplexEntryInFwDataSyncsWithoutIssue()
         crdtEntries.Should().BeEquivalentTo(fwdataEntries,
             options => options
                 .For(e => e.Senses).Exclude(s => s.Order)
+                .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
                 .For(e => e.Components).Exclude(c => c.Id)
                 .For(e => e.ComplexForms).Exclude(c => c.Id));
 
@@ -321,6 +324,7 @@ await crdtApi.CreateEntry(new Entry()
         crdtEntries.Should().BeEquivalentTo(fwdataEntries,
             options => options
                 .For(e => e.Senses).Exclude(s => s.Order)
+                .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
                 .For(e => e.Components).Exclude(c => c.Id)
                 .For(e => e.ComplexForms).Exclude(c => c.Id));
     }
@@ -404,6 +408,7 @@ await crdtApi.CreateEntry(new Entry()
         crdtEntries.Should().BeEquivalentTo(fwdataEntries,
             options => options
                 .For(e => e.Senses).Exclude(s => s.Order)
+                .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
                 .For(e => e.Components).Exclude(c => c.Id)
                 .For(e => e.ComplexForms).Exclude(c => c.Id));
     }
@@ -430,6 +435,7 @@ public async Task UpdatingAnEntryInEachProjectSyncsAcrossBoth()
         crdtEntries.Should().BeEquivalentTo(fwdataEntries,
             options => options
                 .For(e => e.Senses).Exclude(s => s.Order)
+                .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
                 .For(e => e.Components).Exclude(c => c.Id)
                 //todo the headword should be changed
                 .For(e => e.Components).Exclude(c => c.ComponentHeadword)
@@ -501,6 +507,7 @@ public async Task AddingASenseToAnEntryInEachProjectSyncsAcrossBoth()
         crdtEntries.Should().BeEquivalentTo(fwdataEntries,
             options => options
                 .For(e => e.Senses).Exclude(s => s.Order)
+                .For(e => e.Senses).For(s => s.ExampleSentences).Exclude(s => s.Order)
                 .For(e => e.Components).Exclude(c => c.Id)
                 .For(e => e.ComplexForms).Exclude(c => c.Id));
     }
diff --git a/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs b/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
index c9811602c..3f5a7ebf7 100644
--- a/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
+++ b/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
@@ -252,9 +252,9 @@ public Task RemoveSemanticDomainFromSense(Guid senseId, Guid semanticDomainId)
         return api.GetExampleSentence(entryId, senseId, id);
     }
 
-    public Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence)
+    public Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence, BetweenPosition? position = null)
     {
-        DryRunRecords.Add(new DryRunRecord(nameof(CreateExampleSentence), $"Create example sentence {exampleSentence.Sentence}"));
+        DryRunRecords.Add(new DryRunRecord(nameof(CreateExampleSentence), $"Create example sentence {exampleSentence.Sentence} between {position?.Previous} and {position?.Next}"));
         return Task.FromResult(exampleSentence);
     }
 
@@ -278,6 +278,12 @@ public Task<ExampleSentence> UpdateExampleSentence(Guid entryId,
         return Task.FromResult(after);
     }
 
+    public Task MoveExampleSentence(Guid entryId, Guid senseId, Guid exampleId, BetweenPosition between)
+    {
+        DryRunRecords.Add(new DryRunRecord(nameof(MoveExampleSentence), $"Move example sentence {exampleId} between {between.Previous} and {between.Next}"));
+        return Task.CompletedTask;
+    }
+
     public Task DeleteExampleSentence(Guid entryId, Guid senseId, Guid exampleSentenceId)
     {
         DryRunRecords.Add(new DryRunRecord(nameof(DeleteExampleSentence), $"Delete example sentence {exampleSentenceId}"));
diff --git a/backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs b/backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
index b8556c429..dc442429d 100644
--- a/backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
+++ b/backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
@@ -5,6 +5,7 @@
 using LcmCrdt;
 using Microsoft.JSInterop;
 using MiniLcm;
+using MiniLcm.Attributes;
 using MiniLcm.Models;
 using Reinforced.Typings;
 using Reinforced.Typings.Ast.Dependency;
@@ -50,15 +51,9 @@ private static void ConfigureMiniLcmTypes(ConfigurationBuilder builder)
         builder.ExportAsThirdParty<MultiString>().WithName("IMultiString").Imports([
             new() { From = "$lib/dotnet-types/i-multi-string", Target = "type {IMultiString}" }
         ]);
-        builder.ExportAsInterface<Sense>().WithPublicNonStaticProperties(exportBuilder =>
-        {
-            if (exportBuilder.Member.Name == nameof(Sense.Order))
-            {
-                exportBuilder.Ignore();
-            }
-        });
         builder.ExportAsInterfaces([
                 typeof(Entry),
+                typeof(Sense),
                 typeof(ExampleSentence),
                 typeof(WritingSystem),
                 typeof(WritingSystems),
@@ -66,10 +61,15 @@ private static void ConfigureMiniLcmTypes(ConfigurationBuilder builder)
                 typeof(SemanticDomain),
                 typeof(ComplexFormType),
                 typeof(ComplexFormComponent),
-
                 typeof(MiniLcmJsInvokable.MiniLcmFeatures),
             ],
-            exportBuilder => exportBuilder.WithPublicNonStaticProperties());
+            exportBuilder => exportBuilder.WithPublicNonStaticProperties(exportBuilder =>
+        {
+            if (exportBuilder.Member.GetCustomAttribute<MiniLcmInternalAttribute>() is not null)
+            {
+                exportBuilder.Ignore();
+            }
+        }));
         builder.ExportAsEnum<WritingSystemType>().UseString();
         builder.ExportAsInterface<MiniLcmJsInvokable>()
             .FlattenHierarchy()
diff --git a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyChangeModels.verified.txt b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyChangeModels.verified.txt
index 578529de3..3cc5eabdd 100644
--- a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyChangeModels.verified.txt
+++ b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyChangeModels.verified.txt
@@ -123,6 +123,10 @@
     {
       DerivedType: SetOrderChange<Sense>,
       TypeDiscriminator: SetOrderChange:Sense
+    },
+    {
+      DerivedType: SetOrderChange<ExampleSentence>,
+      TypeDiscriminator: SetOrderChange:ExampleSentence
     }
   ],
   IgnoreUnrecognizedTypeDiscriminators: false,
diff --git a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyDbModel.verified.txt b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyDbModel.verified.txt
index e33723359..03cc283de 100644
--- a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyDbModel.verified.txt
+++ b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyDbModel.verified.txt
@@ -117,6 +117,7 @@
     Properties: 
       Id (Guid) Required PK AfterSave:Throw ValueGenerated.OnAdd
       DeletedAt (DateTimeOffset?)
+      Order (double) Required
       Reference (string)
       SenseId (Guid) Required FK Index
       Sentence (MultiString) Required
@@ -208,7 +209,7 @@
           Relational:ColumnType: jsonb
       SnapshotId (no field, Guid?) Shadow FK Index
     Navigations: 
-      ExampleSentences (IList<ExampleSentence>) Collection ToDependent ExampleSentence
+      ExampleSentences (List<ExampleSentence>) Collection ToDependent ExampleSentence
     Keys: 
       Id PK
     Foreign keys: 
diff --git a/backend/FwLite/LcmCrdt/Changes/CreateExampleSentenceChange.cs b/backend/FwLite/LcmCrdt/Changes/CreateExampleSentenceChange.cs
index d99dbe6c4..b0f3ef029 100644
--- a/backend/FwLite/LcmCrdt/Changes/CreateExampleSentenceChange.cs
+++ b/backend/FwLite/LcmCrdt/Changes/CreateExampleSentenceChange.cs
@@ -12,6 +12,7 @@ public CreateExampleSentenceChange(ExampleSentence exampleSentence, Guid senseId
     {
         exampleSentence.Id = EntityId;
         SenseId = senseId;
+        Order = exampleSentence.Order;
         Sentence = exampleSentence.Sentence;
         Translation = exampleSentence.Translation;
         Reference = exampleSentence.Reference;
@@ -24,6 +25,7 @@ private CreateExampleSentenceChange(Guid entityId, Guid senseId) : base(entityId
     }
 
     public Guid SenseId { get; init; }
+    public double Order { get; set; }
     public MultiString? Sentence { get; set; }
     public MultiString? Translation { get; set; }
     public string? Reference { get; set; }
@@ -34,6 +36,7 @@ public override async ValueTask<ExampleSentence> NewEntity(Commit commit, Change
         {
             Id = EntityId,
             SenseId = SenseId,
+            Order = Order,
             Sentence = Sentence ?? new MultiString(),
             Translation = Translation ?? new MultiString(),
             Reference = Reference,
diff --git a/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs b/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
index 8279b9e0a..a3e6716ec 100644
--- a/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
+++ b/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
@@ -343,7 +343,7 @@ private IEnumerable<IChange> CreateEntryChanges(Entry entry, Dictionary<Guid, Se
         {
             yield return addComplexFormTypeChange;
         }
-        var i = 1;
+        var senseOrder = 1;
         foreach (var sense in entry.Senses)
         {
             sense.SemanticDomains = sense.SemanticDomains
@@ -357,10 +357,14 @@ private IEnumerable<IChange> CreateEntryChanges(Entry entry, Dictionary<Guid, Se
             }
             if (sense.Order != default) // we don't anticipate this being necessary, so we'll be strict for now
                 throw new InvalidOperationException("Order should not be provided when creating a sense");
-            sense.Order = i++;
+            sense.Order = senseOrder++;
             yield return new CreateSenseChange(sense, entry.Id);
+            var exampleOrder = 1;
             foreach (var exampleSentence in sense.ExampleSentences)
             {
+                if (exampleSentence.Order != default) // we don't anticipate this being necessary, so we'll be strict for now
+                    throw new InvalidOperationException("Order should not be provided when creating an example sentence");
+                exampleSentence.Order = exampleOrder++;
                 yield return new CreateExampleSentenceChange(exampleSentence, sense.Id);
             }
         }
@@ -479,10 +483,13 @@ private async IAsyncEnumerable<IChange> CreateSenseChanges(Guid entryId, Sense s
         }
 
         yield return new CreateSenseChange(sense, entryId);
-        foreach (var change in sense.ExampleSentences.Select(sentence =>
-                     new CreateExampleSentenceChange(sentence, sense.Id)))
+        var exampleOrder = 1;
+        foreach (var exampleSentence in sense.ExampleSentences)
         {
-            yield return change;
+            if (exampleSentence.Order != default) // we don't anticipate this being necessary, so we'll be strict for now
+                throw new InvalidOperationException("Order should not be provided when creating an example sentence");
+            exampleSentence.Order = exampleOrder++;
+            yield return new CreateExampleSentenceChange(exampleSentence, sense.Id);
         }
     }
 
@@ -547,9 +554,14 @@ public async Task RemoveSemanticDomainFromSense(Guid senseId, Guid semanticDomai
 
     public async Task<ExampleSentence> CreateExampleSentence(Guid entryId,
         Guid senseId,
-        ExampleSentence exampleSentence)
+        ExampleSentence exampleSentence,
+        BetweenPosition? between = null)
     {
         await validators.ValidateAndThrow(exampleSentence);
+        if (exampleSentence.Order != default) // we don't anticipate this being necessary, so we'll be strict for now
+            throw new InvalidOperationException("Order should not be provided when creating an example sentence");
+
+        exampleSentence.Order = await OrderPicker.PickOrder(ExampleSentences.Where(s => s.SenseId == senseId), between);
         await dataModel.AddChange(ClientId, new CreateExampleSentenceChange(exampleSentence, senseId));
         return await dataModel.GetLatest<ExampleSentence>(exampleSentence.Id) ?? throw new NullReferenceException();
     }
@@ -583,6 +595,12 @@ public async Task<ExampleSentence> UpdateExampleSentence(Guid entryId,
         return await GetExampleSentence(entryId, senseId, after.Id) ?? throw new NullReferenceException();
     }
 
+    public async Task MoveExampleSentence(Guid entryId, Guid senseId, Guid exampleId, BetweenPosition between)
+    {
+        var order = await OrderPicker.PickOrder(ExampleSentences.Where(s => s.SenseId == senseId), between);
+        await dataModel.AddChange(ClientId, new Changes.SetOrderChange<ExampleSentence>(exampleId, order));
+    }
+
     public async Task DeleteExampleSentence(Guid entryId, Guid senseId, Guid exampleSentenceId)
     {
         await dataModel.AddChange(ClientId, new DeleteChange<ExampleSentence>(exampleSentenceId));
diff --git a/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs b/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
index 5a7478296..c9131e10f 100644
--- a/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
+++ b/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
@@ -199,7 +199,9 @@ public static void ConfigureCrdt(CrdtConfig config)
             .Add<RemoveComplexFormTypeChange>()
             .Add<SetComplexFormComponentChange>()
             .Add<CreateComplexFormType>()
-            .Add<Changes.SetOrderChange<Sense>>();
+            .Add<Changes.SetOrderChange<Sense>>()
+            .Add<Changes.SetOrderChange<ExampleSentence>>()
+            ;
     }
 
     public static Type[] AllChangeTypes()
diff --git a/backend/FwLite/LcmCrdt/QueryHelpers.cs b/backend/FwLite/LcmCrdt/QueryHelpers.cs
index b2564ba58..ae07b18d6 100644
--- a/backend/FwLite/LcmCrdt/QueryHelpers.cs
+++ b/backend/FwLite/LcmCrdt/QueryHelpers.cs
@@ -5,6 +5,10 @@ public static class QueryHelpers
     public static void ApplySortOrder(this Entry entry)
     {
         entry.Senses.ApplySortOrder();
+        foreach (var sense in entry.Senses)
+        {
+            sense.ExampleSentences.ApplySortOrder();
+        }
     }
 
     public static void ApplySortOrder<T>(this List<T> items) where T : IOrderable
diff --git a/backend/FwLite/MiniLcm.Tests/UpdateEntryTestsBase.cs b/backend/FwLite/MiniLcm.Tests/UpdateEntryTestsBase.cs
index 1c7e21886..eb32a57b9 100644
--- a/backend/FwLite/MiniLcm.Tests/UpdateEntryTestsBase.cs
+++ b/backend/FwLite/MiniLcm.Tests/UpdateEntryTestsBase.cs
@@ -109,7 +109,7 @@ public async Task UpdateEntry_CanUseSameVersionMultipleTimes()
 
     [Theory]
     [InlineData("a,b", "a,b,c,d", "1,2,3,4")] // append
-    [InlineData("a,2", "c,a,b", "0,1,2")] // single prepend
+    [InlineData("a,b", "c,a,b", "0,1,2")] // single prepend
     [InlineData("a,b", "d,c,a,b", "0,0.5,1,2")] // multi prepend
     [InlineData("a,b,c,d", "d,a,b,c", "0,1,2,3")] // move to back
     [InlineData("a,b,c,d", "b,c,d,a", "2,3,4,5")] // move to front
@@ -121,9 +121,9 @@ public async Task UpdateEntry_CanReorderSenses(string before, string after, stri
         // arrange
         var entryId = Guid.NewGuid();
         var senseIds = before.Split(',').Concat(after.Split(',')).Distinct()
-            .ToDictionary(i => i, _ => Guid.NewGuid());
-        var beforeSenses = before.Split(',').Select(i => new Sense() { Id = senseIds[i], EntryId = entryId, Gloss = { { "en", i } } }).ToList();
-        var afterSenses = after.Split(',').Select(i => new Sense() { Id = senseIds[i], EntryId = entryId, Gloss = { { "en", i } } }).ToList();
+            .ToDictionary(@char => @char, _ => Guid.NewGuid());
+        var beforeSenses = before.Split(',').Select(@char => new Sense() { Id = senseIds[@char], EntryId = entryId, Gloss = { { "en", @char } } }).ToList();
+        var afterSenses = after.Split(',').Select(@char => new Sense() { Id = senseIds[@char], EntryId = entryId, Gloss = { { "en", @char } } }).ToList();
 
         var beforeEntry = await Api.CreateEntry(new()
         {
@@ -158,4 +158,67 @@ public async Task UpdateEntry_CanReorderSenses(string before, string after, stri
             actualOrderValues.Should().Be(expectedOrderValues);
         }
     }
+
+    [Theory]
+    [InlineData("a,b", "a,b,c,d", "1,2,3,4")] // append
+    [InlineData("a,b", "c,a,b", "0,1,2")] // single prepend
+    [InlineData("a,b", "d,c,a,b", "0,0.5,1,2")] // multi prepend
+    [InlineData("a,b,c,d", "d,a,b,c", "0,1,2,3")] // move to back
+    [InlineData("a,b,c,d", "b,c,d,a", "2,3,4,5")] // move to front
+    [InlineData("a,b,c,d,e", "a,b,e,c,d", "1,2,2.5,3,4")] // move to middle
+    [InlineData("a,b,c", "c,b,a", "3,4,5")] // reverse
+    [InlineData("a,b,c,d", "d,b,c,a", "1,2,3,4")] // swap
+    public async Task UpdateEntry_CanReorderExampleSentence(string before, string after, string expectedOrderValues)
+    {
+        // arrange
+        var entryId = Guid.NewGuid();
+        var senseId = Guid.NewGuid();
+        var exampleIds = before.Split(',').Concat(after.Split(',')).Distinct()
+            .ToDictionary(@char => @char, _ => Guid.NewGuid());
+        var beforeExamples = before.Split(',').Select(@char => new ExampleSentence() { Id = exampleIds[@char], SenseId = senseId, Sentence = { { "en", @char } } }).ToList();
+        var afterExamples = after.Split(',').Select(@char => new ExampleSentence() { Id = exampleIds[@char], SenseId = senseId, Sentence = { { "en", @char } } }).ToList();
+
+        var beforeEntry = await Api.CreateEntry(new()
+        {
+            Id = entryId,
+            LexemeForm = { { "en", "order" } },
+            Senses = [
+                new Sense
+                {
+                    Id = senseId,
+                    EntryId = entryId,
+                    ExampleSentences = beforeExamples,
+                }
+            ]
+        });
+        var beforeSense = beforeEntry!.Senses[0];
+
+        var afterEntry = beforeEntry!.Copy();
+        var afterSense = afterEntry.Senses[0];
+        afterSense.ExampleSentences = afterExamples;
+
+        // sanity checks
+        beforeSense.ExampleSentences.Should().BeEquivalentTo(beforeExamples, options => options.WithStrictOrdering());
+        if (!ApiUsesImplicitOrdering)
+        {
+            beforeSense.ExampleSentences.Select(s => s.Order).Should()
+                .BeEquivalentTo(Enumerable.Range(1, beforeExamples.Count), options => options.WithStrictOrdering());
+        }
+
+        // act
+        await Api.UpdateEntry(beforeEntry, afterEntry);
+        var actualEntry = await Api.GetEntry(afterEntry.Id);
+        var actual = actualEntry!.Senses[0];
+
+        // assert
+        actual.Should().NotBeNull();
+        actual.ExampleSentences.Should().BeEquivalentTo(afterSense.ExampleSentences,
+            options => options.WithStrictOrdering().Excluding(s => s.Order));
+
+        if (!ApiUsesImplicitOrdering)
+        {
+            var actualOrderValues = string.Join(',', actual.ExampleSentences.Select(s => s.Order.ToString(CultureInfo.GetCultureInfo("en-US"))));
+            actualOrderValues.Should().Be(expectedOrderValues);
+        }
+    }
 }
diff --git a/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs b/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs
index 1dfe7c287..b8ad8f887 100644
--- a/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs
+++ b/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs
@@ -47,6 +47,13 @@ Task<WritingSystem> UpdateWritingSystem(WritingSystemId id,
     #endregion
 
     #region Sense
+    /// <summary>
+    /// Creates the provided sense and adds it to the specified entry
+    /// </summary>
+    /// <param name="entryId">The ID of the sense's parent entry</param>
+    /// <param name="sesnse">The sense to create</param>
+    /// <param name="position">Where the sense should be inserted in the entry's list of senses. If null it will be appended to the end of the list.</param>
+    /// <returns></returns>
     Task<Sense> CreateSense(Guid entryId, Sense sense, BetweenPosition? position = null);
     Task<Sense> UpdateSense(Guid entryId, Guid senseId, UpdateObjectInput<Sense> update);
     Task<Sense> UpdateSense(Guid entryId, Sense before, Sense after);
@@ -57,7 +64,15 @@ Task<WritingSystem> UpdateWritingSystem(WritingSystemId id,
     #endregion
 
     #region ExampleSentence
-    Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence);
+    /// <summary>
+    /// Creates the provided example sentence and adds it to the specified sense
+    /// </summary>
+    /// <param name="entryId">The ID of the sense's parent entry</param>
+    /// <param name="senseId">The ID of example sentence's parent sense</param>
+    /// <param name="exampleSentence">The example sentence to create</param>
+    /// <param name="position">Where the example sentence should be inserted in the sense's list of example sentences. If null it will be appended to the end of the list.</param>
+    /// <returns></returns>
+    Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence, BetweenPosition? position = null);
     Task<ExampleSentence> UpdateExampleSentence(Guid entryId,
         Guid senseId,
         Guid exampleSentenceId,
@@ -66,6 +81,7 @@ Task<ExampleSentence> UpdateExampleSentence(Guid entryId,
         Guid senseId,
         ExampleSentence before,
         ExampleSentence after);
+    Task MoveExampleSentence(Guid entryId, Guid senseId, Guid exampleSentenceId, BetweenPosition position);
 
     Task DeleteExampleSentence(Guid entryId, Guid senseId, Guid exampleSentenceId);
     #endregion
diff --git a/backend/FwLite/MiniLcm/Models/ExampleSentence.cs b/backend/FwLite/MiniLcm/Models/ExampleSentence.cs
index dde441994..14e3352be 100644
--- a/backend/FwLite/MiniLcm/Models/ExampleSentence.cs
+++ b/backend/FwLite/MiniLcm/Models/ExampleSentence.cs
@@ -1,8 +1,12 @@
-namespace MiniLcm.Models;
+using MiniLcm.Attributes;
 
-public class ExampleSentence : IObjectWithId
+namespace MiniLcm.Models;
+
+public class ExampleSentence : IObjectWithId, IOrderable
 {
     public virtual Guid Id { get; set; }
+    [MiniLcmInternal]
+    public double Order { get; set; }
     public virtual MultiString Sentence { get; set; } = new();
     public virtual MultiString Translation { get; set; } = new();
     public virtual string? Reference { get; set; } = null;
@@ -26,6 +30,7 @@ public IObjectWithId Copy()
         return new ExampleSentence()
         {
             Id = Id,
+            Order = Order,
             DeletedAt = DeletedAt,
             SenseId = SenseId,
             Sentence = Sentence.Copy(),
diff --git a/backend/FwLite/MiniLcm/Models/Sense.cs b/backend/FwLite/MiniLcm/Models/Sense.cs
index d774a0fae..4464dee00 100644
--- a/backend/FwLite/MiniLcm/Models/Sense.cs
+++ b/backend/FwLite/MiniLcm/Models/Sense.cs
@@ -14,7 +14,7 @@ public class Sense : IObjectWithId, IOrderable
     public virtual string PartOfSpeech { get; set; } = string.Empty;
     public virtual Guid? PartOfSpeechId { get; set; }
     public virtual IList<SemanticDomain> SemanticDomains { get; set; } = [];
-    public virtual IList<ExampleSentence> ExampleSentences { get; set; } = [];
+    public virtual List<ExampleSentence> ExampleSentences { get; set; } = [];
 
     public Guid[] GetReferences()
     {
diff --git a/backend/FwLite/MiniLcm/SyncHelpers/ExampleSentenceSync.cs b/backend/FwLite/MiniLcm/SyncHelpers/ExampleSentenceSync.cs
index c2cdf2d2e..d663318a7 100644
--- a/backend/FwLite/MiniLcm/SyncHelpers/ExampleSentenceSync.cs
+++ b/backend/FwLite/MiniLcm/SyncHelpers/ExampleSentenceSync.cs
@@ -11,7 +11,7 @@ public static async Task<int> Sync(Guid entryId,
         IList<ExampleSentence> afterExampleSentences,
         IMiniLcmApi api)
     {
-        return await DiffCollection.Diff(
+        return await DiffCollection.DiffOrderable(
             beforeExampleSentences,
             afterExampleSentences,
             new ExampleSentencesDiffApi(api, entryId, senseId));
@@ -50,21 +50,27 @@ public static async Task<int> Sync(Guid entryId,
         return new UpdateObjectInput<ExampleSentence>(patchDocument);
     }
 
-    private class ExampleSentencesDiffApi(IMiniLcmApi api, Guid entryId, Guid senseId) : ObjectWithIdCollectionDiffApi<ExampleSentence>
+    private class ExampleSentencesDiffApi(IMiniLcmApi api, Guid entryId, Guid senseId) : IOrderableCollectionDiffApi<ExampleSentence>
     {
-        public override async Task<int> Add(ExampleSentence afterExampleSentence)
+        public async Task<int> Add(ExampleSentence afterExampleSentence, BetweenPosition between)
         {
-            await api.CreateExampleSentence(entryId, senseId, afterExampleSentence);
+            await api.CreateExampleSentence(entryId, senseId, afterExampleSentence, between);
             return 1;
         }
 
-        public override async Task<int> Remove(ExampleSentence beforeExampleSentence)
+        public async Task<int> Move(ExampleSentence example, BetweenPosition between)
+        {
+            await api.MoveExampleSentence(entryId, senseId, example.Id, between);
+            return 1;
+        }
+
+        public async Task<int> Remove(ExampleSentence beforeExampleSentence)
         {
             await api.DeleteExampleSentence(entryId, senseId, beforeExampleSentence.Id);
             return 1;
         }
 
-        public override Task<int> Replace(ExampleSentence beforeExampleSentence, ExampleSentence afterExampleSentence)
+        public Task<int> Replace(ExampleSentence beforeExampleSentence, ExampleSentence afterExampleSentence)
         {
             return Sync(entryId, senseId, beforeExampleSentence, afterExampleSentence, api);
         }
diff --git a/frontend/viewer/src/HomeView.svelte b/frontend/viewer/src/HomeView.svelte
index 5b9695cf3..0ce6f4cb6 100644
--- a/frontend/viewer/src/HomeView.svelte
+++ b/frontend/viewer/src/HomeView.svelte
@@ -176,13 +176,15 @@
                 </ListItem>
               </AnchorListItem>
             {/each}
-            <AnchorListItem href={`/testing/project-view`}>
-              <ListItem title="Test Project" icon={mdiTestTube}>
-                <div slot="actions" class="pointer-events-none">
-                  <Button icon={mdiChevronRight} class="p-2"/>
-                </div>
-              </ListItem>
-            </AnchorListItem>
+            <DevContent>
+              <AnchorListItem href={`/testing/project-view`}>
+                <ListItem title="Test Project" icon={mdiTestTube}>
+                  <div slot="actions" class="pointer-events-none">
+                    <Button icon={mdiChevronRight} class="p-2"/>
+                  </div>
+                </ListItem>
+              </AnchorListItem>
+            </DevContent>
             {#if !projects.some(p => p.name === exampleProjectName) || $isDev}
               <ListItem title="Create Example Project" on:click={() => createProject(exampleProjectName)}>
                 <div slot="actions" class="pointer-events-none">
diff --git a/frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.svelte b/frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.svelte
index b7d7334cc..7ec9d9603 100644
--- a/frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.svelte
+++ b/frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.svelte
@@ -14,7 +14,7 @@
   import { createEventDispatcher, getContext } from 'svelte';
   import { useLexboxApi } from '../services/service-provider';
   import { deriveAsync } from '../utils/time';
-  import { defaultSense, firstDef, firstGloss, glosses, headword, randomId } from '../utils';
+  import { defaultSense, firstDef, firstGloss, glosses, headword } from '../utils';
   import { useProjectCommands } from '../commands';
   import type { SaveHandler } from '../services/save-event-service';
   import {SortField} from '$lib/dotnet-types';
@@ -76,7 +76,7 @@
   }
 
   async function onClickAddSense(entry: IEntry): Promise<void> {
-    const newSense = defaultSense(randomId());
+    const newSense = defaultSense(entry.id);
     const savedSense = await saveHandler(() => lexboxApi.createSense(entry.id, newSense));
     entry.senses = [...entry.senses, savedSense];
     selectedSense = savedSense;
diff --git a/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte b/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte
index a3a4c3720..1331c9b00 100644
--- a/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte
+++ b/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte
@@ -24,6 +24,7 @@
   async function createEntry(e: Event, closeDialog: () => void) {
     e.preventDefault();
     loading = true;
+    console.debug('Creating entry', entry);
     await saveHandler(() => lexboxApi.createEntry(entry));
     dispatch('created', {entry});
     if (requester) {
diff --git a/frontend/viewer/src/lib/entry-editor/object-editors/EntryEditor.svelte b/frontend/viewer/src/lib/entry-editor/object-editors/EntryEditor.svelte
index 00837a772..531ba11b9 100644
--- a/frontend/viewer/src/lib/entry-editor/object-editors/EntryEditor.svelte
+++ b/frontend/viewer/src/lib/entry-editor/object-editors/EntryEditor.svelte
@@ -26,13 +26,13 @@
   export let entry: IEntry;
 
   function addSense() {
-    const sense = defaultSense();
+    const sense = defaultSense(entry.id);
     highlightedEntity = sense;
     entry.senses = [...entry.senses, sense];
   }
 
   function addExample(sense: ISense) {
-    const sentence = defaultExampleSentence();
+    const sentence = defaultExampleSentence(sense.id);
     highlightedEntity = sentence;
     sense.exampleSentences = [...sense.exampleSentences, sentence];
     entry = entry; // examples counts are not updated without this
diff --git a/frontend/viewer/src/lib/utils.ts b/frontend/viewer/src/lib/utils.ts
index babfbf95e..18cb1d68a 100644
--- a/frontend/viewer/src/lib/utils.ts
+++ b/frontend/viewer/src/lib/utils.ts
@@ -1,6 +1,6 @@
-import type { IEntry, IExampleSentence, IMultiString, ISense, IWritingSystem, IWritingSystems } from '$lib/dotnet-types';
+import type {IEntry, IExampleSentence, IMultiString, ISense, IWritingSystem, IWritingSystems} from '$lib/dotnet-types';
 
-import type { WritingSystemSelection } from './config-types';
+import type {WritingSystemSelection} from './config-types';
 
 export function firstVal(multi: IMultiString): string | undefined {
   return Object.values(multi).find(value => !!value);
@@ -82,14 +82,6 @@ export function pickWritingSystems(
   return [];
 }
 
-const emptyIdPrefix = '00000000-0000-0000-0000-';
-export function emptyId(): string {
-  return emptyIdPrefix + crypto.randomUUID().slice(emptyIdPrefix.length);
-}
-export function isEmptyId(id: string): boolean {
-  return id.startsWith(emptyIdPrefix);
-}
-
 export function randomId(): string {
   return crypto.randomUUID();
 }
@@ -108,10 +100,10 @@ export function defaultEntry(): IEntry {
   };
 }
 
-export function defaultSense(id?: string): ISense {
+export function defaultSense(entryId: string): ISense {
   return {
-    id: id ?? emptyId(),
-    entryId: '',
+    id: randomId(),
+    entryId,
     definition: {},
     gloss: {},
     partOfSpeechId: undefined,
@@ -121,10 +113,10 @@ export function defaultSense(id?: string): ISense {
   };
 }
 
-export function defaultExampleSentence(): IExampleSentence {
+export function defaultExampleSentence(senseId: string): IExampleSentence {
   return {
-    id: emptyId(),
-    senseId: '',
+    id: randomId(),
+    senseId,
     sentence: {},
     translation: {},
     reference: '',