diff --git a/.changelog/11666.txt b/.changelog/11666.txt
new file mode 100644
index 0000000000..65080a0681
--- /dev/null
+++ b/.changelog/11666.txt
@@ -0,0 +1,3 @@
+```release-note:feature
+ui: Upgrade Lock Sessions to use partitions
+```
diff --git a/ui/packages/consul-ui/app/components/composite-row/index.scss b/ui/packages/consul-ui/app/components/composite-row/index.scss
index 2f56cbe05c..2612d0ca12 100644
--- a/ui/packages/consul-ui/app/components/composite-row/index.scss
+++ b/ui/packages/consul-ui/app/components/composite-row/index.scss
@@ -1,5 +1,4 @@
 @import './layout';
-@import './skin';
 %composite-row {
   @extend %list-row;
 }
@@ -33,12 +32,8 @@
 .consul-auth-method-list > ul > li:not(:first-child) {
   @extend %with-composite-row-intent;
 }
-.consul-lock-session-list ul > li:not(:first-child) {
-  @extend %with-one-action-row;
-}
 // TODO: This hides the iconless dt's in the below lists as they don't have
 // tooltips the todo would be to wrap these texts in spans
-.consul-lock-session-list ul > li:not(:first-child) dl:not([class]) dt,
 .consul-nspace-list > ul > li:not(:first-child) dt,
 .consul-token-list > ul > li:not(:first-child) dt,
 .consul-policy-list > ul li:not(:first-child) dl:not(.datacenter) dt,
diff --git a/ui/packages/consul-ui/app/components/composite-row/layout.scss b/ui/packages/consul-ui/app/components/composite-row/layout.scss
index fd3b465a5f..b855ef108e 100644
--- a/ui/packages/consul-ui/app/components/composite-row/layout.scss
+++ b/ui/packages/consul-ui/app/components/composite-row/layout.scss
@@ -7,11 +7,6 @@
     'header actions'
     'detail actions';
 }
-%with-one-action-row {
-  @extend %composite-row;
-  grid-template-columns: 1fr auto;
-  padding-right: 12px;
-}
 %composite-row-header {
   grid-area: header;
   align-self: start;
diff --git a/ui/packages/consul-ui/app/components/composite-row/skin.scss b/ui/packages/consul-ui/app/components/composite-row/skin.scss
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/form/README.mdx b/ui/packages/consul-ui/app/components/consul/lock-session/form/README.mdx
new file mode 100644
index 0000000000..8c6a372e91
--- /dev/null
+++ b/ui/packages/consul-ui/app/components/consul/lock-session/form/README.mdx
@@ -0,0 +1,31 @@
+# Consul::LockSession::Form
+
+A component for rendering and deleting/invalidating a Lock Session.
+
+The form is fully functional and will delete/invalidate and show its
+notifications when pressing the delete/invalidate button.
+
+```hbs preview-template
+<DataSource
+  @src={{uri "/partition/default/dc-1/sessions/for-key/my-kv"}} as |source|>
+  {{#if source.data.ID}}
+    <Consul::LockSession::Form
+      @item={{source.data}}
+      @ondelete={{noop}}
+    />
+  {{/if}}
+</DataSource>
+```
+
+## Arguments
+
+| Argument/Attribute | Type | Default | Description |
+| --- | --- | --- | --- |
+| `item` | `array` |  | A Lock Session |
+| `ondelete` | `function` |  | An action to confirm when the `delete` (or Invalidate) action is clicked and confirmed |
+
+## See
+
+- [Template Source Code](./index.hbs)
+
+---
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs b/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs
index d3102b79e8..ddb5a5b1a4 100644
--- a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs
+++ b/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs
@@ -1,63 +1,106 @@
-<DataForm
-  @dc={{dc}}
-  @nspace={{nspace}}
-  @partition={{partition}}
-  @item={{item}}
-  @type="session"
-  @onsubmit={{action onsubmit}}
-  as |api|
+<div
+  class="consul-lock-session-form"
+  data-test-session={{@item.ID}}
+  ...attributes
 >
-  <BlockSlot @name="form">
-      <div
-        class="consul-lock-session-form definition-table"
-        data-test-session={{api.data.ID}}
-        ...attributes
-      >
-        <h2>
-          <a href="{{env 'CONSUL_DOCS_URL'}}/internals/sessions.html#session-design" rel="help noopener noreferrer" target="_blank">Lock Session</a>
-        </h2>
-        <dl>
-{{#if api.data.Name}}
-          <dt>Name</dt>
-          <dd>{{api.data.Name}}</dd>
-{{/if}}
-          <dt>ID</dt>
-          <dd>{{api.data.ID}}</dd>
-          <dt>Node</dt>
-          <dd>
-              <a href={{href-to 'dc.nodes.show' api.data.Node}}>{{api.data.Node}}</a>
-          </dd>
-          <dt>Delay</dt>
-          <dd>{{duration-from api.data.LockDelay}}</dd>
-          <dt>TTL</dt>
-          <dd>{{or api.data.TTL '-'}}</dd>
-          <dt>Behavior</dt>
-          <dd>{{api.data.Behavior}}</dd>
-{{#let api.data.checks as |checks|}}
-          <dt>Health Checks</dt>
-          <dd>
-  {{#if (gt checks.length 0)}}
-              {{ join ', ' checks}}
-  {{else}}
-              -
+  <DataWriter
+    @sink={{uri
+      '/${partition}/${nspace}/${dc}/session'
+      (hash
+        partition=@item.Partition
+        nspace=@item.Namespace
+        dc=@item.Datacenter
+      )
+    }}
+    @type={{'session'}}
+    @label={{'Lock Session'}}
+    @ondelete={{fn (if @ondelete @ondelete @onsubmit) @item}}
+    @onchange={{fn (optional @onsubmit) @item}}
+    as |writer|>
+      <BlockSlot @name="removed" as |after|>
+        <Consul::LockSession::Notifications
+          {{notification
+            after=(action after)
+          }}
+          @type="remove"
+        />
+      </BlockSlot>
+      <BlockSlot @name="error" as |after error|>
+        <Consul::LockSession::Notifications
+          {{notification
+            after=(action after)
+          }}
+          @type="remove"
+          @error={{error}}
+        />
+      </BlockSlot>
+      <BlockSlot @name="content">
+        <div
+          class="definition-table"
+        >
+          <dl>
+  {{#if @item.Name}}
+            <dt>Name</dt>
+            <dd>{{@item.Name}}</dd>
   {{/if}}
-          </dd>
-{{/let}}
-        </dl>
-{{#if (can 'delete session' item=api.data)}}
-        <ConfirmationDialog @message="Are you sure you want to invalidate this session?">
+            <dt>ID</dt>
+            <dd>{{@item.ID}}</dd>
+            <dt>Node</dt>
+            <dd>
+                <a
+                  href={{href-to 'dc.nodes.show' @item.Node}}
+                >
+                  {{@item.Node}}
+                </a>
+            </dd>
+            <dt>Delay</dt>
+            <dd>{{duration-from @item.LockDelay}}</dd>
+            <dt>TTL</dt>
+            <dd>{{or @item.TTL '-'}}</dd>
+            <dt>Behavior</dt>
+            <dd>{{@item.Behavior}}</dd>
+  {{#let @item.checks as |checks|}}
+            <dt>Health Checks</dt>
+            <dd>
+    {{#if (gt checks.length 0)}}
+                {{ join ', ' checks}}
+    {{else}}
+                -
+    {{/if}}
+            </dd>
+  {{/let}}
+          </dl>
+        </div>
+  {{#if (can 'delete session' item=@item)}}
+        <ConfirmationDialog @message="Are you sure you want to invalidate this Lock Session?">
           <BlockSlot @name="action" as |confirm|>
-            <button type="button" data-test-delete class="type-delete" {{action confirm api.delete session}} disabled={{api.disabled}}>Invalidate Session</button>
+            <Action
+              data-test-delete
+              class="type-delete"
+              {{on 'click' (fn confirm (fn writer.delete @item))}}
+            >
+              Invalidate Session
+            </Action>
           </BlockSlot>
           <BlockSlot @name="dialog" as |execute cancel message|>
             <p>
               {{message}}
             </p>
-            <button type="button" class="type-delete" {{action execute}}>Confirm Invalidation</button>
-            <button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
+            <Action
+              class="type-delete"
+              {{on 'click' (fn execute)}}
+            >
+              Confirm Invalidation
+            </Action>
+            <Action
+              class="type-cancel"
+              {{on 'click' (fn cancel)}}
+            >
+              Cancel
+            </Action>
           </BlockSlot>
         </ConfirmationDialog>
-{{/if}}
-      </div>
-    </BlockSlot>
-</DataForm>
\ No newline at end of file
+  {{/if}}
+      </BlockSlot>
+    </DataWriter>
+</div>
\ No newline at end of file
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.js b/ui/packages/consul-ui/app/components/consul/lock-session/form/index.js
deleted file mode 100644
index 5570647734..0000000000
--- a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Component from '@ember/component';
-
-export default Component.extend({});
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.scss b/ui/packages/consul-ui/app/components/consul/lock-session/form/index.scss
index 3dab35b60b..acb23ebdd1 100644
--- a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.scss
+++ b/ui/packages/consul-ui/app/components/consul/lock-session/form/index.scss
@@ -1,9 +1,3 @@
 .consul-lock-session-form {
-  h2 {
-    @extend %h200;
-    border-bottom: var(--decor-border-200);
-    border-color: rgb(var(--tone-gray-200));
-    padding-bottom: 0.2em;
-    margin-bottom: 0.5em;
-  }
+  overflow: hidden;
 }
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/list/README.mdx b/ui/packages/consul-ui/app/components/consul/lock-session/list/README.mdx
index 5344c81688..bc9321fced 100644
--- a/ui/packages/consul-ui/app/components/consul/lock-session/list/README.mdx
+++ b/ui/packages/consul-ui/app/components/consul/lock-session/list/README.mdx
@@ -1,29 +1,26 @@
----
-class: ember
----
 # Consul::LockSession::List
 
+A presentational component for rendering Node Lock Sessions
+
 ```hbs preview-template
-<DataSource @src="/partition/default/dc-1/sessions/for-node/my-node" as |source|>
+<DataSource
+  @src={{uri "/partition/default/dc-1/sessions/for-node/my-node"}} as |source|>
   <Consul::LockSession::List
     @items={{source.data}}
-    @onInvalidate={{action (noop)}}
+    @ondelete={{action (noop)}}
   />
 </DataSource>
 ```
 
-A presentational component for rendering Node Lock Sessions
-
-### Arguments
+## Arguments
 
 | Argument/Attribute | Type | Default | Description |
 | --- | --- | --- | --- |
 | `items` | `array` |  | An array of Node Lock Sessions  |
-| `onInvalidate` | `function` |  | An action to confirm when the `Invalidate` action is clicked and confirmed |
+| `ondelete` | `function` |  | An action to confirm when the `delete` (or Invalidate) action is clicked and confirmed |
 
-### See
+## See
 
-- [Component Source Code](./index.js)
 - [Template Source Code](./index.hbs)
 
 ---
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs b/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs
index 450474ba48..cf7f826901 100644
--- a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs
+++ b/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs
@@ -1,5 +1,8 @@
-{{#if (gt items.length 0)}}
-<ListCollection @items={{items}} class="consul-lock-session-list" as |item index|>
+<ListCollection
+  class="consul-lock-session-list"
+  ...attributes
+  @items={{@items}}
+as |item index|>
   <BlockSlot @name="header">
   {{#if item.Name}}
     <span>{{item.Name}}</span>
@@ -35,7 +38,9 @@
       <dd data-test-session-delay>{{duration-from item.LockDelay}}</dd>
     </dl>
     <dl class="ttl">
-      <dt {{tooltip}}>
+      <dt
+        {{tooltip}}
+      >
         TTL
       </dt>
     {{#if (eq item.TTL "")}}
@@ -45,14 +50,18 @@
     {{/if}}
     </dl>
     <dl class="behavior">
-      <dt {{tooltip}}>
+      <dt
+        {{tooltip}}
+      >
         Behavior
       </dt>
       <dd>{{item.Behavior}}</dd>
     </dl>
 {{#let (union item.NodeChecks item.ServiceChecks) as |checks|}}
     <dl class="checks">
-      <dt {{tooltip}}>
+      <dt
+        {{tooltip}}
+      >
         Checks
       </dt>
       <dd>
@@ -67,27 +76,36 @@
     </dl>
 {{/let}}
   </BlockSlot>
-{{#if (can "delete sessions")}}
   <BlockSlot @name="actions">
-    <ConfirmationDialog @message="Are you sure you want to invalidate this session?">
+    <ConfirmationDialog
+      @message="Are you sure you want to invalidate this session?"
+    >
       <BlockSlot @name="action" as |confirm|>
-          <button data-test-delete
-            type="button"
-            class="type-delete"
-            onclick={{action confirm onInvalidate item}}
-          >
-            Invalidate
-          </button>
+        <Action
+          data-test-delete
+          class="type-delete"
+          {{on 'click' (fn confirm (fn @ondelete item))}}
+        >
+          Invalidate
+        </Action>
       </BlockSlot>
       <BlockSlot @name="dialog" as |execute cancel message|>
-          <p>
-              {{message}}
-          </p>
-          <button type="button" class="type-delete" onclick={{action execute}}>Confirm Invalidate</button>
-          <button type="button" class="type-cancel" onclick={{action cancel}}>Cancel</button>
+        <p>
+          {{message}}
+        </p>
+        <Action
+          class="type-delete"
+          {{on 'click' (fn execute)}}
+        >
+          Confirm Invalidate
+        </Action>
+        <Action
+          class="type-cancel"
+          {{on 'click' (fn cancel)}}
+        >
+          Cancel
+        </Action>
       </BlockSlot>
     </ConfirmationDialog>
   </BlockSlot>
-{{/if}}
-</ListCollection>
-{{/if}}
\ No newline at end of file
+</ListCollection>
\ No newline at end of file
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.js b/ui/packages/consul-ui/app/components/consul/lock-session/list/index.js
deleted file mode 100644
index 4798652642..0000000000
--- a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import Component from '@ember/component';
-
-export default Component.extend({
-  tagName: '',
-});
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.scss b/ui/packages/consul-ui/app/components/consul/lock-session/list/index.scss
index 7d7c82efe2..79327901d1 100644
--- a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.scss
+++ b/ui/packages/consul-ui/app/components/consul/lock-session/list/index.scss
@@ -1,10 +1,16 @@
+.consul-lock-session-list ul > li:not(:first-child) {
+  @extend %composite-row;
+}
+.consul-lock-session-list button {
+  /* knock the button over a little */
+  /* for meatball menus we use as much clickable space */
+  /* as possible which is invisible hence why we need to */
+  /* do this for a single button */
+  margin-right: var(--horizontal-padding);
+}
+.consul-lock-session-list dl {
+  @extend %horizontal-kv-list;
+}
 .consul-lock-session-list .checks dd {
-  display: inline-flex;
-  flex-wrap: wrap;
-  padding-left: 0px;
-}
-.consul-lock-session-list .checks dd > *:not(:last-child)::after {
-  content: ',';
-  margin-right: 0.3em;
-  display: inline;
+  @extend %csv-list;
 }
diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/notifications/index.hbs b/ui/packages/consul-ui/app/components/consul/lock-session/notifications/index.hbs
index 52b97172c3..8653429328 100644
--- a/ui/packages/consul-ui/app/components/consul/lock-session/notifications/index.hbs
+++ b/ui/packages/consul-ui/app/components/consul/lock-session/notifications/index.hbs
@@ -1,7 +1,36 @@
-{{#if (eq @type 'delete')}}
-  {{#if (eq @status 'success') }}
-    The session was invalidated.
+{{#if (eq @type 'remove')}}
+  {{#if @error}}
+    <Notice
+      class="notification-delete"
+      @type="error"
+      ...attributes
+    as |notice|>
+      <notice.Header>
+        <strong>Error!</strong>
+      </notice.Header>
+      <notice.Body>
+        <p>
+          There was an error invalidating the Lock Session.
+      {{#if (and @error.status @error.detail)}}
+          <br />{{@error.status}}: {{@error.detail}}
+      {{/if}}
+        </p>
+      </notice.Body>
+    </Notice>
   {{else}}
-    There was an error invalidating the session.
+    <Notice
+      class="notification-delete"
+      @type="success"
+      ...attributes
+    as |notice|>
+      <notice.Header>
+        <strong>Success!</strong>
+      </notice.Header>
+      <notice.Body>
+        <p>
+          Your Lock Session has been invalidated.
+        </p>
+      </notice.Body>
+    </Notice>
   {{/if}}
 {{/if}}
\ No newline at end of file
diff --git a/ui/packages/consul-ui/app/components/data-loader/chart.xstate.js b/ui/packages/consul-ui/app/components/data-loader/chart.xstate.js
index 94cc961316..dde7c5349c 100644
--- a/ui/packages/consul-ui/app/components/data-loader/chart.xstate.js
+++ b/ui/packages/consul-ui/app/components/data-loader/chart.xstate.js
@@ -17,9 +17,15 @@ export default {
         target: 'loading',
       },
     ],
+    INVALIDATE: [
+      {
+        target: 'invalidating',
+      },
+    ],
   },
   states: {
     load: {},
+    invalidating: {},
     loading: {
       on: {
         SUCCESS: {
diff --git a/ui/packages/consul-ui/app/components/data-loader/index.hbs b/ui/packages/consul-ui/app/components/data-loader/index.hbs
index b62b7c3c38..b29d59a584 100644
--- a/ui/packages/consul-ui/app/components/data-loader/index.hbs
+++ b/ui/packages/consul-ui/app/components/data-loader/index.hbs
@@ -7,6 +7,7 @@
 {{#let (hash
   data=data
   error=error
+  invalidate=(action "invalidate")
   dispatchError=(queue (action (mut error) value="error.errors.firstObject") (action dispatch "ERROR"))
 ) as |api|}}
 
@@ -16,7 +17,7 @@
       {{! if we didn't specify any data}}
       {{#if (not items)}}
         {{! try and load the data if we aren't in an error state}}
-        <State @notMatches={{array "error" "disconnected"}}>
+        <State @notMatches={{array "error" "disconnected" "invalidating"}}>
           {{! but only if we only asked for a single load and we are in loading state}}
           {{#if (and src (or (not once) (state-matches state "loading")))}}
             <DataSource
@@ -46,7 +47,7 @@
     {{/yield-slot}}
   </State>
 
-  <State @matches={{array "idle" "disconnected"}}>
+  <State @matches={{array "idle" "disconnected" "invalidating"}}>
 
     <State @matches="disconnected">
         {{#yield-slot name="disconnected" params=(block-params (action dispatch "RESET"))}}
diff --git a/ui/packages/consul-ui/app/components/data-loader/index.js b/ui/packages/consul-ui/app/components/data-loader/index.js
index 19967a12ac..0039ba3d49 100644
--- a/ui/packages/consul-ui/app/components/data-loader/index.js
+++ b/ui/packages/consul-ui/app/components/data-loader/index.js
@@ -1,5 +1,6 @@
 import Component from '@ember/component';
 import { set } from '@ember/object';
+import { schedule } from '@ember/runloop';
 import Slotted from 'block-slots';
 
 import chart from './chart.xstate';
@@ -21,6 +22,12 @@ export default Component.extend(Slotted, {
     this.dispatch('LOAD');
   },
   actions: {
+    invalidate() {
+      this.dispatch('INVALIDATE');
+      schedule('afterRender', () => {
+        this.dispatch('LOAD');
+      });
+    },
     isLoaded: function() {
       return typeof this.items !== 'undefined' || typeof this.src === 'undefined';
     },
diff --git a/ui/packages/consul-ui/app/components/data-writer/index.hbs b/ui/packages/consul-ui/app/components/data-writer/index.hbs
index 048b775e08..53af6c8565 100644
--- a/ui/packages/consul-ui/app/components/data-writer/index.hbs
+++ b/ui/packages/consul-ui/app/components/data-writer/index.hbs
@@ -90,7 +90,7 @@ as |after|}}
 {{#let
   (action dispatch "RESET")
 as |after|}}
-    {{#yield-slot name="error" params=(block-params after)}}
+    {{#yield-slot name="error" params=(block-params after api.error)}}
       {{yield api}}
     {{else}}
       <Notice
diff --git a/ui/packages/consul-ui/app/components/definition-table/layout.scss b/ui/packages/consul-ui/app/components/definition-table/layout.scss
index b4ffaebc51..50762c223c 100644
--- a/ui/packages/consul-ui/app/components/definition-table/layout.scss
+++ b/ui/packages/consul-ui/app/components/definition-table/layout.scss
@@ -2,6 +2,8 @@
   display: grid;
   grid-template-columns: 140px auto;
   grid-gap: 0.4em 20px;
+}
+%definition-table > dl {
   margin-bottom: 1.4em;
 }
 %definition-table dd > * {
diff --git a/ui/packages/consul-ui/app/components/list-collection/index.hbs b/ui/packages/consul-ui/app/components/list-collection/index.hbs
index a3eb31252a..178144444e 100644
--- a/ui/packages/consul-ui/app/components/list-collection/index.hbs
+++ b/ui/packages/consul-ui/app/components/list-collection/index.hbs
@@ -22,7 +22,7 @@
       <li
         data-test-list-row
         onclick={{action 'click'}} style={{{cell.style}}}
-        class={{if (not linkable) 'linkable' (if (is linkable item=cell.item) 'linkable')}}
+        class={{if linkable (if (is linkable item=cell.item) 'linkable')}}
       >
         <YieldSlot @name="header"><div class="header">{{yield cell.item cell.index}}</div></YieldSlot>
         <YieldSlot @name="details"><div class="detail">{{yield cell.item cell.index}}</div></YieldSlot>
diff --git a/ui/packages/consul-ui/app/components/list-row/layout.scss b/ui/packages/consul-ui/app/components/list-row/layout.scss
index 8440596fa9..e14d32045b 100644
--- a/ui/packages/consul-ui/app/components/list-row/layout.scss
+++ b/ui/packages/consul-ui/app/components/list-row/layout.scss
@@ -1,8 +1,16 @@
 %list-row {
-  padding-top: 10px;
-  padding-bottom: 10px;
+  /* this can be reused by internal components */
+  /* for positioning if required */
+  --horizontal-padding: 12px;
+  --vertical-padding: 10px;
+  padding: var(--vertical-padding) 0;
   /* whilst this isn't in the designs this makes our temporary rollover look better */
-  padding-left: 12px;
+  /* it doesn't happen on the right as we use a larger hit area with our */
+  /* meatball menu which would overlap this and the meatball is the most */
+  /* right hand 'action' button */
+  padding-left: var(--horizontal-padding);
+  /* once we have our scroll pane refresh we no longer need a padding for */
+  /* shadowing/rollover purposes so a lot of that ^ can go */
 }
 %list-row-detail,
 %list-row-header-icon {
diff --git a/ui/packages/consul-ui/app/components/list-row/skin.scss b/ui/packages/consul-ui/app/components/list-row/skin.scss
index 0aacf59bf1..654e6e8947 100644
--- a/ui/packages/consul-ui/app/components/list-row/skin.scss
+++ b/ui/packages/consul-ui/app/components/list-row/skin.scss
@@ -8,7 +8,8 @@
 }
 %list-row-intent {
   border-color: rgb(var(--tone-gray-200));
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  /*TODO: This should use a shared/CSS prop shadow*/
+  box-shadow: 0 2px 4px rgb(var(--black) / 10%);
   border-top-color: var(--transparent);
   cursor: pointer;
 }
diff --git a/ui/packages/consul-ui/app/routes/dc/nodes/show/sessions.js b/ui/packages/consul-ui/app/routes/dc/nodes/show/sessions.js
deleted file mode 100644
index ed951db9fd..0000000000
--- a/ui/packages/consul-ui/app/routes/dc/nodes/show/sessions.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import Route from 'consul-ui/routing/route';
-import { inject as service } from '@ember/service';
-import { action } from '@ember/object';
-
-import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
-
-export default class SessionsRoute extends Route.extend(WithBlockingActions) {
-  @service('repository/session') sessionRepo;
-  @service('feedback') feedback;
-
-  @action
-  invalidateSession(item) {
-    const route = this;
-    return this.feedback.execute(() => {
-      return this.sessionRepo.remove(item).then(() => {
-        route.refresh();
-      });
-    }, 'delete');
-  }
-}
diff --git a/ui/packages/consul-ui/app/serializers/kv.js b/ui/packages/consul-ui/app/serializers/kv.js
index 82436601de..b7dd2eaf91 100644
--- a/ui/packages/consul-ui/app/serializers/kv.js
+++ b/ui/packages/consul-ui/app/serializers/kv.js
@@ -16,7 +16,18 @@ export default class KvSerializer extends Serializer {
 
   respondForQueryRecord(respond, query) {
     return super.respondForQueryRecord(
-      cb => respond((headers, body) => cb(headers, body[0])),
+      cb =>
+        respond((headers, body) => {
+          // If item.Session is not set make sure we overwrite any existing one.
+          // Using @replace, defaultValue or similar model apporaches does not work
+          // as if a property is undefined ember-data just ignores it instead of
+          // deleting the value of the existing property.
+          if (typeof body[0].Session === 'undefined') {
+            body[0].Session = '';
+          }
+          //
+          return cb(headers, body[0]);
+        }),
       query
     );
   }
diff --git a/ui/packages/consul-ui/app/serializers/session.js b/ui/packages/consul-ui/app/serializers/session.js
index 119619cea6..b37fe6ff2b 100644
--- a/ui/packages/consul-ui/app/serializers/session.js
+++ b/ui/packages/consul-ui/app/serializers/session.js
@@ -7,7 +7,20 @@ export default class SessionSerializer extends Serializer {
 
   respondForQueryRecord(respond, query) {
     return super.respondForQueryRecord(
-      cb => respond((headers, body) => cb(headers, body[0])),
+      cb =>
+        respond((headers, body) => {
+          if (body.length === 0) {
+            const e = new Error();
+            e.errors = [
+              {
+                status: '404',
+                title: 'Not found',
+              },
+            ];
+            throw e;
+          }
+          return cb(headers, body[0]);
+        }),
       query
     );
   }
diff --git a/ui/packages/consul-ui/app/styles/layout.scss b/ui/packages/consul-ui/app/styles/layout.scss
index 6c689ec61f..1fe6db81fa 100644
--- a/ui/packages/consul-ui/app/styles/layout.scss
+++ b/ui/packages/consul-ui/app/styles/layout.scss
@@ -53,8 +53,18 @@ html[data-route$='edit'] .app-view > header + div > *:first-child {
 %app-view-content .container {
   margin-top: 1.25em;
 }
-.consul-upstream-instance-list,
-.consul-lock-session-list {
+
+%list-after-secondary-nav {
+  margin-top: 0 !important;
+}
+%list-after-secondary-nav ul {
+  border-top-width: 0 !important;
+}
+%list-after-filter-bar {
+  border-top-width: 0 !important;
+}
+
+.consul-upstream-instance-list {
   margin-top: 0 !important;
 }
 /* turn off top borders for things flush up to a filter bar */
@@ -62,12 +72,11 @@ html[data-route='dc.services.index'] .consul-service-list ul,
 .consul-nspace-list ul,
 .consul-service-instance-list ul,
 .consul-node-list ul,
-.consul-lock-session-list ul,
 .consul-role-list ul,
 .consul-policy-list ul,
 .consul-token-list ul,
 .consul-auth-method-list ul {
-  border-top-width: 0 !important;
+  @extend %list-after-filter-bar;
 }
 .notice + .consul-token-list ul {
   border-top-width: 1px !important;
diff --git a/ui/packages/consul-ui/app/styles/routes/dc/kv/index.scss b/ui/packages/consul-ui/app/styles/routes/dc/kv/index.scss
index e55f58ebdf..47f238569a 100644
--- a/ui/packages/consul-ui/app/styles/routes/dc/kv/index.scss
+++ b/ui/packages/consul-ui/app/styles/routes/dc/kv/index.scss
@@ -2,3 +2,10 @@ html[data-route^='dc.kv'] .type-toggle {
   float: right;
   margin-bottom: 0 !important;
 }
+html[data-route^='dc.kv.edit'] h2 {
+  @extend %h200;
+  border-bottom: var(--decor-border-200);
+  border-color: rgb(var(--tone-gray-200));
+  padding-bottom: 0.2em;
+  margin-bottom: 0.5em;
+}
diff --git a/ui/packages/consul-ui/app/styles/routes/dc/nodes/index.scss b/ui/packages/consul-ui/app/styles/routes/dc/nodes/index.scss
index 09c09f9f53..aa433dc904 100644
--- a/ui/packages/consul-ui/app/styles/routes/dc/nodes/index.scss
+++ b/ui/packages/consul-ui/app/styles/routes/dc/nodes/index.scss
@@ -1,3 +1,6 @@
 html[data-route^='dc.nodes.show.metadata'] table tr {
   cursor: default;
 }
+html[data-route^='dc.nodes.show.sessions'] .consul-lock-session-list {
+  @extend %list-after-secondary-nav;
+}
diff --git a/ui/packages/consul-ui/app/styles/typography.scss b/ui/packages/consul-ui/app/styles/typography.scss
index c3e5a7420c..2e9bd6370f 100644
--- a/ui/packages/consul-ui/app/styles/typography.scss
+++ b/ui/packages/consul-ui/app/styles/typography.scss
@@ -11,6 +11,9 @@ fieldset > header,
 %form-element > span {
   @extend %h400;
 }
+%definition-table dt {
+  line-height: var(--typo-lead-700);
+}
 %internal-button,
 %breadcrumbs li > *,
 %tab-nav {
diff --git a/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs b/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs
index a375e575f0..b84750d099 100644
--- a/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs
+++ b/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs
@@ -39,11 +39,17 @@ as |parentKey|}}
 as |dc partition nspace item|}}
 
   <AppView>
+
     <BlockSlot @name="breadcrumbs">
       <ol>
-          <li>
-            <a data-test-back href={{href-to 'dc.kv.index'}}>Key / Values</a>
-          </li>
+        <li>
+          <Action
+            data-test-back
+            @href={{href-to 'dc.kv.index'}}
+          >
+            Key / Values
+          </Action>
+        </li>
 {{#if (not-eq parentKey separator)}}
 
 {{#let
@@ -56,8 +62,8 @@ as |parts|}}
         {{! to make the correct href. 'Enough' is the current index plus 1.}}
         {{! We push on a '' here so make sure we get a trailing slash/separator }}
           <li>
-            <a
-              href={{href-to 'dc.kv.folder'
+            <Action
+              @href={{href-to 'dc.kv.folder'
                   (join '/'
                     (append
                       (slice 0 (add index 1) parts) ''
@@ -66,7 +72,7 @@ as |parts|}}
               }}
             >
               {{breadcrumb}}
-            </a>
+            </Action>
           </li>
       {{/if}}
     {{/each}}
@@ -75,18 +81,21 @@ as |parts|}}
 {{/if}}
       </ol>
     </BlockSlot>
+
     <BlockSlot @name="header">
         <h1>
-  {{#if (and item.Key (not-eq item.Key parentKey))}}
+{{#if (and item.Key (not-eq item.Key parentKey))}}
           <route.Title @title="Edit Key / Value" @render={{false}} />
           {{left-trim item.Key parentKey}}
-  {{else}}
+{{else}}
           <route.Title @title="New Key / Value" @render={{true}} />
-  {{/if}}
+{{/if}}
         </h1>
     </BlockSlot>
+
     <BlockSlot @name="content">
-    {{! if a KV has a session `Session` will always be populated despite any specific session permissions }}
+
+{{! if a KV has a session `Session` will always be populated despite any specific session permissions }}
 {{#if item.Session}}
       <Notice
         @type="warning"
@@ -99,6 +108,7 @@ as |parts|}}
         </notice.Body>
       </Notice>
 {{/if}}
+
       <Consul::Kv::Form
         @item={{item}}
         @dc={{route.params.dc}}
@@ -108,6 +118,7 @@ as |parts|}}
         @parent={{parentKey}}
       />
 
+
 {{! `session` is slightly different to `item.Session` as we only have `session` }}
 {{! if you have `session:read perms` whereas you can get the sessions ID from }}
 {{! `item.Session` without any session perms }}
@@ -123,17 +134,21 @@ as |parts|}}
         }}
         @onchange={{action (mut session) value="data"}}
       />
-  {{#if session}}
-      {{!FIXME}}
+      <h2>
+        <Action
+          rel="help"
+          @href={{concat (env 'CONSUL_DOCS_URL') '/internals/sessions.html#session-design'}}
+          @external={{true}}
+        >
+          Lock Session
+        </Action>
+      </h2>
+  {{#if session.ID}}
       <Consul::LockSession::Form
         @item={{session}}
-        @dc={{route.params.dc}}
-        @nspace={{route.params.nspace}}
-        @partition={{route.params.partition}}
-        @onsubmit={{action (noop) undefined}}
+        @ondelete={{loader.invalidate}}
       />
   {{/if}}
-
 {{/if}}
 
     </BlockSlot>
diff --git a/ui/packages/consul-ui/app/templates/dc/nodes/show/sessions.hbs b/ui/packages/consul-ui/app/templates/dc/nodes/show/sessions.hbs
index 72dcb360cb..b868871f78 100644
--- a/ui/packages/consul-ui/app/templates/dc/nodes/show/sessions.hbs
+++ b/ui/packages/consul-ui/app/templates/dc/nodes/show/sessions.hbs
@@ -18,38 +18,97 @@ as |route|>
     </BlockSlot>
 
     <BlockSlot @name="loaded">
-{{#let api.data as |sessions|}}
-  <div class="tab-section">
-  {{#if (gt sessions.length 0)}}
-      <Consul::LockSession::List
-        @items={{sessions}}
-        @onInvalidate={{action send 'invalidateSession'}}
-      />
-  {{else}}
-      <EmptyState
-        @login={{route.model.app.login.open}}
-      >
-        <BlockSlot @name="header">
-          <h2>
-              Welcome to Lock Sessions
-          </h2>
+{{#let api.data as |items|}}
+    <div class="tab-section">
+      <DataWriter
+        @sink={{uri '/${partition}/${dc}/${nspace}/session/'
+          (hash
+            partition=route.params.partition
+            nspace=route.params.nspace
+            dc=route.params.dc
+          )
+        }}
+        @type="session"
+        @label="Lock Session"
+        @ondelete={{refresh-route}}
+      as |writer|>
+
+        <BlockSlot @name="removed" as |after|>
+          <Consul::LockSession::Notifications
+            {{notification
+              after=(action after)
+            }}
+            @type="remove"
+          />
         </BlockSlot>
-        <BlockSlot @name="body">
-          <p>
-            Consul provides a session mechanism which can be used to build distributed locks. Sessions act as a binding layer between nodes, health checks, and key/value data. There are currently no lock sessions present, or you may not have permission to view lock sessions.
-          </p>
+
+        <BlockSlot @name="error" as |after error|>
+          <Consul::LockSession::Notifications
+            {{notification
+              after=(action after)
+            }}
+            @type="remove"
+            @error={{error}}
+          />
         </BlockSlot>
-        <BlockSlot @name="actions">
-          <li class="docs-link">
-            <a href="{{env 'CONSUL_DOCS_URL'}}/internals/sessions.html" rel="noopener noreferrer" target="_blank">Documentation on sessions</a>
-          </li>
-          <li class="learn-link">
-            <a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/tutorials/consul/distributed-semaphore" rel="noopener noreferrer" target="_blank">Read the guide</a>
-          </li>
+
+        <BlockSlot @name="content">
+
+          <DataCollection
+            @type="session"
+            @items={{items}}
+          as |collection|>
+
+            <collection.Collection>
+              <Consul::LockSession::List
+                @items={{collection.items}}
+                @ondelete={{writer.delete}}
+              />
+            </collection.Collection>
+
+            <collection.Empty>
+              <EmptyState
+                @login={{route.model.app.login.open}}
+              >
+                <BlockSlot @name="header">
+                  <h2>
+                      Welcome to Lock Sessions
+                  </h2>
+                </BlockSlot>
+
+                <BlockSlot @name="body">
+                  <p>
+                    Consul provides a session mechanism which can be used to build distributed locks. Sessions act as a binding layer between nodes, health checks, and key/value data. There are currently no lock sessions present, or you may not have permission to view lock sessions.
+                  </p>
+                </BlockSlot>
+
+                <BlockSlot @name="actions">
+                  <li class="docs-link">
+                    <Action
+                      @href="{{env 'CONSUL_DOCS_URL'}}/internals/sessions.html"
+                      @external={{true}}
+                    >
+                      Documentation on Lock Sessions
+                    </Action>
+                  </li>
+                  <li class="learn-link">
+                    <Action
+                      @href="{{env 'CONSUL_DOCS_LEARN_URL'}}/tutorials/consul/distributed-semaphore"
+                      @external={{true}}
+                    >
+                      Read the guide
+                    </Action>
+                  </li>
+                </BlockSlot>
+
+              </EmptyState>
+            </collection.Empty>
+
+          </DataCollection>
+
         </BlockSlot>
-      </EmptyState>
-  {{/if}}
-  </div>
+      </DataWriter>
+    </div>
 {{/let}}
   </BlockSlot>
 </DataLoader>
diff --git a/ui/packages/consul-ui/mock-api/v1/session/info/_ b/ui/packages/consul-ui/mock-api/v1/session/info/_
index 77a1f99e47..99effb6be1 100644
--- a/ui/packages/consul-ui/mock-api/v1/session/info/_
+++ b/ui/packages/consul-ui/mock-api/v1/session/info/_
@@ -6,6 +6,10 @@
           typeof location.search.ns !== 'undefined' ? location.search.ns :
             typeof http.body.Namespace !== 'undefined' ? http.body.Namespace : 'default'
         }",
+        "Partition": "${
+          typeof location.search.partition !== 'undefined' ? location.search.partition :
+            typeof http.body.Partition !== 'undefined' ? http.body.Partition : 'default'
+        }",
         "Node":"node-1",
         "NodeChecks":["serfHealth"],
         "ServiceChecks": [
diff --git a/ui/packages/consul-ui/tests/acceptance/dc/kvs/sessions/invalidate.feature b/ui/packages/consul-ui/tests/acceptance/dc/kvs/sessions/invalidate.feature
index 1b3672dc00..6959dba8b7 100644
--- a/ui/packages/consul-ui/tests/acceptance/dc/kvs/sessions/invalidate.feature
+++ b/ui/packages/consul-ui/tests/acceptance/dc/kvs/sessions/invalidate.feature
@@ -28,5 +28,5 @@ Feature: dc / kvs / sessions / invalidate: Invalidate Lock Sessions
     And I click delete on the session
     And I click confirmDelete on the session
     Then the url should be /datacenter/kv/key/edit
-    And "[data-notification]" has the "notification-update" class
+    And "[data-notification]" has the "notification-delete" class
     And "[data-notification]" has the "error" class