diff --git a/src/main.module.ts b/src/main.module.ts
index 7c0b4504e7ac41bf2e358fea9c0f30777e595880..4084dfff617d595e92159587f68503c29aff8d3e 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -42,6 +42,7 @@ import {HttpClientModule} from "@angular/common/http";
 import { EffectsModule } from "@ngrx/effects";
 import { UseEffects } from "./services/effect/effect";
 import { MatDialogModule, MatTabsModule } from "@angular/material";
+import { ViewerStateUseEffect } from "./services/state/viewerState.store";
 
 @NgModule({
   imports : [
@@ -62,6 +63,7 @@ import { MatDialogModule, MatTabsModule } from "@angular/material";
     TooltipModule.forRoot(),
     TabsModule.forRoot(),
     EffectsModule.forRoot([
+      ViewerStateUseEffect,
       UseEffects
     ]),
     StoreModule.forRoot({
diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts
index 0b1edc38bcf23d9001d9554392d83c9e26789432..90f7e42b8d52edd4ec6da8f396024b1f5ef9ee44 100644
--- a/src/services/state/viewerState.store.ts
+++ b/src/services/state/viewerState.store.ts
@@ -1,6 +1,10 @@
-import { Action } from '@ngrx/store'
+import { Action, Store, select } from '@ngrx/store'
 import { UserLandmark } from 'src/atlasViewer/atlasViewer.apiService.service';
 import { NgLayerInterface } from 'src/atlasViewer/atlasViewer.component';
+import { Injectable } from '@angular/core';
+import { Actions, Effect, ofType } from '@ngrx/effects';
+import { withLatestFrom, map, shareReplay, startWith, tap } from 'rxjs/operators';
+import { Observable } from 'rxjs';
 
 export interface ViewerStateInterface{
   fetchedTemplates : any[]
@@ -34,13 +38,16 @@ export interface AtlasAction extends Action{
   deselectLandmarks : UserLandmark[]
 
   navigation? : any
+
+  payload: any
 }
 
 export function viewerState(
   state:Partial<ViewerStateInterface> = {
     landmarksSelected : [],
     fetchedTemplates : [],
-    loadedNgLayers: []
+    loadedNgLayers: [],
+    userLandmarks: []
   },
   action:AtlasAction
 ){
@@ -174,3 +181,78 @@ export const DESELECT_LANDMARKS = `DESELECT_LANDMARKS`
 export const USER_LANDMARKS = `USER_LANDMARKS`
 
 export const NEHUBA_LAYER_CHANGED = `NEHUBA_LAYER_CHANGED`
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class ViewerStateUseEffect{
+  constructor(
+    private actions$: Actions,
+    private store$: Store<any>
+  ){
+    this.currentLandmarks$ = this.store$.pipe(
+      select('viewerState'),
+      select('userLandmarks'),
+      shareReplay(1),
+    )
+
+    this.removeUserLandmarks = this.actions$.pipe(
+      ofType(ACTION_TYPES.REMOVE_USER_LANDMARKS),
+      withLatestFrom(this.currentLandmarks$),
+      map(([action, currentLandmarks]) => {
+        const { landmarkIds } = (action as AtlasAction).payload
+        for ( const rmId of landmarkIds ){
+          const idx = currentLandmarks.findIndex(({ id }) => id === rmId)
+          if (idx < 0) console.warn(`remove userlandmark with id ${rmId} does not exist`)
+        }
+        const removeSet = new Set(landmarkIds)
+        return {
+          type: USER_LANDMARKS,
+          landmarks: currentLandmarks.filter(({ id }) => !removeSet.has(id))
+        }
+      })
+    )
+
+    this.addUserLandmarks$ = this.actions$.pipe(
+      ofType(ACTION_TYPES.ADD_USERLANDMARKS),
+      withLatestFrom(this.currentLandmarks$),
+      map(([action, currentLandmarks]) => {
+        const { landmarks } = action as AtlasAction
+        const landmarkMap = new Map()
+        for (const landmark of currentLandmarks) {
+          const { id } = landmark
+          landmarkMap.set(id, landmark)
+        }
+        for (const landmark of landmarks) {
+          const { id } = landmark
+          if (landmarkMap.has(id)) {
+            console.warn(`Attempting to add a landmark that already exists, id: ${id}`)
+          } else {
+            landmarkMap.set(id, landmark)
+          }
+        }
+        const userLandmarks = Array.from(landmarkMap).map(([id, landmark]) => landmark)
+        return {
+          type: USER_LANDMARKS,
+          landmarks: userLandmarks
+        }
+      })
+    )
+  }
+
+  private currentLandmarks$: Observable<any[]>
+
+  @Effect()
+  removeUserLandmarks: Observable<any>
+
+  @Effect()
+  addUserLandmarks$: Observable<any>
+}
+
+const ACTION_TYPES = {
+  ADD_USERLANDMARKS: `ADD_USERLANDMARKS`,
+  REMOVE_USER_LANDMARKS: 'REMOVE_USER_LANDMARKS'
+}
+
+export const VIEWERSTATE_ACTION_TYPES = ACTION_TYPES
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts
index 44dfc715979eabe3d800db61441c919171ecb669..08a9ae3a927d92347388693db595f1d99b63de86 100644
--- a/src/ui/nehubaContainer/nehubaContainer.component.ts
+++ b/src/ui/nehubaContainer/nehubaContainer.component.ts
@@ -3,7 +3,7 @@ import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
 import { Store, select } from "@ngrx/store";
 import { ViewerStateInterface, safeFilter, CHANGE_NAVIGATION, isDefined, USER_LANDMARKS, ADD_NG_LAYER, REMOVE_NG_LAYER, NgViewerStateInterface, MOUSE_OVER_LANDMARK, SELECT_LANDMARKS, Landmark, PointLandmarkGeometry, PlaneLandmarkGeometry, OtherLandmarkGeometry, getNgIds, getMultiNgIdsRegionsLabelIndexMap, generateLabelIndexId } from "../../services/stateStore.service";
 import { Observable, Subscription, fromEvent, combineLatest, merge } from "rxjs";
-import { filter,map, take, scan, debounceTime, distinctUntilChanged, switchMap, skip, withLatestFrom, buffer, tap, throttleTime, bufferTime } from "rxjs/operators";
+import { filter,map, take, scan, debounceTime, distinctUntilChanged, switchMap, skip, withLatestFrom, buffer, tap, throttleTime, bufferTime, startWith } from "rxjs/operators";
 import { AtlasViewerAPIServices, UserLandmark } from "../../atlasViewer/atlasViewer.apiService.service";
 import { timedValues } from "../../util/generator";
 import { AtlasViewerConstantsServices } from "../../atlasViewer/atlasViewer.constantService.service";
@@ -11,7 +11,7 @@ import { ViewerConfiguration } from "src/services/state/viewerConfig.store";
 import { pipeFromArray } from "rxjs/internal/util/pipe";
 import { NEHUBA_READY } from "src/services/state/ngViewerState.store";
 import { MOUSE_OVER_SEGMENTS } from "src/services/state/uiState.store";
-import { SELECT_REGIONS_WITH_ID, NEHUBA_LAYER_CHANGED } from "src/services/state/viewerState.store";
+import { SELECT_REGIONS_WITH_ID, NEHUBA_LAYER_CHANGED, VIEWERSTATE_ACTION_TYPES } from "src/services/state/viewerState.store";
 
 const getProxyUrl = (ngUrl) => `nifti://${BACKEND_URL}preview/file?fileUrl=${encodeURIComponent(ngUrl.replace(/^nifti:\/\//,''))}`
 const getProxyOther = ({source}) => /AUTH_227176556f3c4bb38df9feea4b91200c/.test(source)
@@ -135,8 +135,6 @@ export class NehubaContainer implements OnInit, OnDestroy{
   private landmarksLabelIndexMap : Map<number, any> = new Map()
   private landmarksNameMap : Map<string,number> = new Map()
   
-  private userLandmarks : UserLandmark[] = []
-  
   private subscriptions : Subscription[] = []
   private nehubaViewerSubscriptions : Subscription[] = []
 
@@ -220,13 +218,9 @@ export class NehubaContainer implements OnInit, OnDestroy{
     )
 
     this.userLandmarks$ = this.store.pipe(
-      /* TODO: distinct until changed */
       select('viewerState'),
-      // filter(state => isDefined(state) && isDefined(state.userLandmarks)),
-      map(state => isDefined(state) && isDefined(state.userLandmarks)
-        ? state.userLandmarks
-        : []),
-      distinctUntilChanged(userLmUnchanged)
+      select('userLandmarks'),
+      distinctUntilChanged()
     )
 
     this.onHoverSegments$ = this.store.pipe(
@@ -454,10 +448,7 @@ export class NehubaContainer implements OnInit, OnDestroy{
     )
 
     this.subscriptions.push(
-      this.userLandmarks$.pipe(
-        // distinctUntilChanged((old,new) => )
-      ).subscribe(landmarks => {
-        this.userLandmarks = landmarks
+      this.userLandmarks$.subscribe(landmarks => {
         if(this.nehubaViewer){
           this.nehubaViewer.updateUserLandmarks(landmarks)
         }
@@ -968,14 +959,16 @@ export class NehubaContainer implements OnInit, OnDestroy{
         if(!landmarks.every(l => l.position.constructor === Array) || !landmarks.every(l => l.position.every(v => !isNaN(v))) || !landmarks.every(l => l.position.length == 3))
           throw new Error('position needs to be a length 3 tuple of numbers ')
         this.store.dispatch({
-          type: USER_LANDMARKS,
+          type: VIEWERSTATE_ACTION_TYPES.ADD_USERLANDMARKS,
           landmarks : landmarks
         })
       },
-      remove3DLandmarks : ids => {
+      remove3DLandmarks : landmarkIds => {
         this.store.dispatch({
-          type : USER_LANDMARKS,
-          landmarks : this.userLandmarks.filter(l => ids.findIndex(id => id === l.id) < 0)
+          type: VIEWERSTATE_ACTION_TYPES.REMOVE_USER_LANDMARKS,
+          payload: {
+            landmarkIds
+          }
         })
       },
       hideSegment : (labelIndex) => {