diff --git a/openbis_ng_ui/src/js/components/common/grid/GridController.js b/openbis_ng_ui/src/js/components/common/grid/GridController.js
index 834cb7c654d9635748d228b610849810b0810fb4..d7adbb399edb98d3009cbaab94096161fa4e0554 100644
--- a/openbis_ng_ui/src/js/components/common/grid/GridController.js
+++ b/openbis_ng_ui/src/js/components/common/grid/GridController.js
@@ -11,13 +11,24 @@ export default class GridController {
   init(context) {
     const props = context.getProps()
 
-    const columns = props.columns.map(column => ({
-      ...column,
-      label: column.label || _.upperFirst(column.field),
-      render: column.render || (row => this._getValue(row, column.field)),
-      sort: column.sort === undefined ? true : column.sort,
-      visible: true
-    }))
+    const columns = []
+    let initialSort = null
+    let initialSortDirection = null
+
+    props.columns.forEach(column => {
+      if (column.sort) {
+        initialSort = column.field
+        initialSortDirection = column.sort
+      }
+      columns.push({
+        ...column,
+        field: column.field,
+        label: column.label || _.upperFirst(column.field),
+        render: column.render || (row => this._getValue(row, column.field)),
+        sortable: column.sortable === undefined ? true : column.sortable,
+        visible: true
+      })
+    })
 
     context.initState({
       loaded: false,
@@ -31,8 +42,8 @@ export default class GridController {
       currentRows: [],
       selectedRowId: null,
       selectedRow: null,
-      sort: null,
-      sortDirection: 'asc'
+      sort: initialSort,
+      sortDirection: initialSortDirection
     })
 
     this.context = context
@@ -282,14 +293,23 @@ export default class GridController {
   }
 
   handleSortChange(column) {
-    if (!column.sort) {
+    if (!column.sortable) {
       return
     }
+
     this.context
-      .setState(prevState => ({
-        sort: column.field,
-        sortDirection: prevState.sortDirection === 'asc' ? 'desc' : 'asc'
-      }))
+      .setState(state => {
+        if (column.field === state.sort) {
+          return {
+            sortDirection: state.sortDirection === 'asc' ? 'desc' : 'asc'
+          }
+        } else {
+          return {
+            sort: column.field,
+            sortDirection: 'asc'
+          }
+        }
+      })
       .then(() => {
         this._saveSettings()
         this._recalculateCurrentRows()
diff --git a/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx b/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx
index 96e1daab89afc676e799e92b0d5d24aeb739ceaa..98f15b42ba69387eff4e8f2666c9e735ec4b204c 100644
--- a/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx
+++ b/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx
@@ -30,12 +30,13 @@ class GridHeaderLabel extends React.PureComponent {
     const { column, sort, sortDirection, classes } = this.props
 
     if (column.visible) {
-      if (column.sort) {
+      if (column.sortable) {
+        const active = sort === column.field
         return (
           <TableCell classes={{ root: classes.cell }}>
             <TableSortLabel
-              active={sort === column.field}
-              direction={sortDirection}
+              active={active}
+              direction={active ? sortDirection : 'asc'}
               onClick={this.handleClick}
             >
               {column.label}
diff --git a/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx b/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx
index 3025a88243d1a7726045a810bac9e84e4f76c85c..d9843eb56db7416d92503130bd95b8a1a5de8017 100644
--- a/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx
+++ b/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx
@@ -78,7 +78,8 @@ class VocabularyForm extends React.PureComponent {
           columns={[
             {
               field: 'code.value',
-              label: 'Code'
+              label: 'Code',
+              sort: 'asc'
             },
             {
               field: 'label.value',
diff --git a/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx b/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx
index 1545d3a322e18a81c169dc60fa42704fbd1ce9f4..3bf79c0ae8bdf18e6ab812e440ca61ece014b7f4 100644
--- a/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx
+++ b/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx
@@ -143,10 +143,6 @@ class TypeSearch extends React.Component {
         <Grid
           id={ids.TYPES_GRID_ID}
           columns={[
-            {
-              field: 'permId.entityKind',
-              label: 'Kind'
-            },
             {
               field: 'code',
               render: row => (
@@ -157,7 +153,12 @@ class TypeSearch extends React.Component {
                 >
                   {row.code}
                 </Link>
-              )
+              ),
+              sort: 'asc'
+            },
+            {
+              field: 'permId.entityKind',
+              label: 'Kind'
             },
             {
               field: 'description'
diff --git a/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx b/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx
index 3b7b6a5ab254cfe6f60c3761cc6f4bc8a78b150a..b5c63c61870a332f1e2fef4c260fa760a4699fb7 100644
--- a/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx
+++ b/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx
@@ -64,7 +64,8 @@ class UserSearch extends React.Component {
           id={ids.USERS_GRID_ID}
           columns={[
             {
-              field: 'userId'
+              field: 'userId',
+              sort: 'asc'
             },
             {
               field: 'firstName'
diff --git a/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js b/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js
index 258eb0e59a32e69cc9a0738a0cbcd82abf247ec0..3a838a87ea8562d994247aa0c9b513040ba313fd 100644
--- a/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js
+++ b/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js
@@ -27,6 +27,7 @@ beforeEach(() => {
 describe('VocabularyFormComponent', () => {
   test('load new', testLoadNew)
   test('load existing', testLoadExisting)
+  test('sort', testSort)
   test('select term', testSelectTerm)
   test('add term', testAddTerm)
   test('remove term', testRemoveTerm)
@@ -94,7 +95,7 @@ async function testLoadExisting() {
         filter: {
           value: null
         },
-        sort: null
+        sort: 'asc'
       },
       {
         field: 'label.value',
@@ -213,6 +214,76 @@ async function testLoadExisting() {
   })
 }
 
+async function testSort() {
+  const form = await mountNew()
+
+  const labels = [
+    'Term 1',
+    'term 11',
+    'Term 2',
+    'TERM A',
+    'term B',
+    'Term A1',
+    'tErM A11',
+    'term A2'
+  ]
+
+  for (let i = 0; i < labels.length; i++) {
+    form.getButtons().getAddTerm().click()
+    await form.update()
+    form.getParameters().getTerm().getLabel().change(labels[i])
+    await form.update()
+  }
+
+  form.getGrid().getColumns()[1].getLabel().click()
+  await form.update()
+
+  form.expectJSON({
+    grid: {
+      columns: [
+        { field: 'code.value', sort: null },
+        { field: 'label.value', sort: 'asc' },
+        { field: 'description.value', sort: null },
+        { field: 'official.value', sort: null }
+      ],
+      rows: [
+        { values: { 'label.value': 'Term 1' } },
+        { values: { 'label.value': 'Term 2' } },
+        { values: { 'label.value': 'term 11' } },
+        { values: { 'label.value': 'TERM A' } },
+        { values: { 'label.value': 'Term A1' } },
+        { values: { 'label.value': 'term A2' } },
+        { values: { 'label.value': 'tErM A11' } },
+        { values: { 'label.value': 'term B' } }
+      ]
+    }
+  })
+
+  form.getGrid().getColumns()[1].getLabel().click()
+  await form.update()
+
+  form.expectJSON({
+    grid: {
+      columns: [
+        { field: 'code.value', sort: null },
+        { field: 'label.value', sort: 'desc' },
+        { field: 'description.value', sort: null },
+        { field: 'official.value', sort: null }
+      ],
+      rows: [
+        { values: { 'label.value': 'term B' } },
+        { values: { 'label.value': 'tErM A11' } },
+        { values: { 'label.value': 'term A2' } },
+        { values: { 'label.value': 'Term A1' } },
+        { values: { 'label.value': 'TERM A' } },
+        { values: { 'label.value': 'term 11' } },
+        { values: { 'label.value': 'Term 2' } },
+        { values: { 'label.value': 'Term 1' } }
+      ]
+    }
+  })
+}
+
 async function testSelectTerm() {
   const form = await mountExisting()
 
@@ -286,12 +357,22 @@ async function testAddTerm() {
 
   form.expectJSON({
     grid: {
-      rows: fixture.TEST_VOCABULARY_DTO.terms.map(term => ({
+      columns: [
+        { field: 'code.value', sort: 'asc' },
+        { field: 'label.value', sort: null },
+        { field: 'description.value', sort: null },
+        { field: 'official.value', sort: null }
+      ],
+      rows: [
+        fixture.TEST_TERM_1_DTO,
+        fixture.TEST_TERM_2_DTO,
+        fixture.TEST_TERM_3_DTO,
+        fixture.TEST_TERM_4_DTO,
+        fixture.TEST_TERM_5_DTO,
+        fixture.TEST_TERM_6_DTO
+      ].map(term => ({
         values: {
-          'code.value': term.getCode(),
-          'label.value': term.getLabel(),
-          'description.value': term.getDescription(),
-          'official.value': String(term.isOfficial())
+          'code.value': term.getCode()
         },
         selected: false
       })),
@@ -304,6 +385,9 @@ async function testAddTerm() {
     }
   })
 
+  form.getGrid().getColumns()[0].getLabel().click()
+  await form.update()
+
   form.getGrid().getPaging().getPageSize().change(5)
   await form.update()
 
@@ -315,9 +399,15 @@ async function testAddTerm() {
 
   form.expectJSON({
     grid: {
+      columns: [
+        { field: 'code.value', sort: 'desc' },
+        { field: 'label.value', sort: null },
+        { field: 'description.value', sort: null },
+        { field: 'official.value', sort: null }
+      ],
       rows: [
         {
-          values: { 'code.value': fixture.TEST_TERM_6_DTO.getCode() },
+          values: { 'code.value': fixture.TEST_TERM_1_DTO.getCode() },
           selected: false
         },
         {
@@ -497,16 +587,17 @@ async function testChangeTerm() {
   form.getGrid().getRows()[1].click()
   await form.update()
 
-  form
-    .getGrid()
-    .getRows()[1]
-    .expectJSON({
-      values: {
-        'label.value': fixture.TEST_TERM_2_DTO.getLabel()
-      }
-    })
-
   form.expectJSON({
+    grid: {
+      rows: [
+        { values: { 'label.value': fixture.TEST_TERM_1_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_2_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_3_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_4_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_5_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_6_DTO.getLabel() } }
+      ]
+    },
     parameters: {
       term: {
         title: 'Term',
@@ -526,16 +617,17 @@ async function testChangeTerm() {
   form.getParameters().getTerm().getLabel().change('New Label')
   await form.update()
 
-  form
-    .getGrid()
-    .getRows()[1]
-    .expectJSON({
-      values: {
-        'label.value': 'New Label'
-      }
-    })
-
   form.expectJSON({
+    grid: {
+      rows: [
+        { values: { 'label.value': fixture.TEST_TERM_1_DTO.getLabel() } },
+        { values: { 'label.value': 'New Label' } },
+        { values: { 'label.value': fixture.TEST_TERM_3_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_4_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_5_DTO.getLabel() } },
+        { values: { 'label.value': fixture.TEST_TERM_6_DTO.getLabel() } }
+      ]
+    },
     parameters: {
       term: {
         title: 'Term',
@@ -619,20 +711,14 @@ async function testValidateTerm() {
   form.getGrid().getPaging().getPageSize().change(5)
   await form.update()
 
+  form.getGrid().getPaging().getNextPage().click()
+  await form.update()
+
   form.expectJSON({
     grid: {
-      rows: [
-        fixture.TEST_TERM_1_DTO,
-        fixture.TEST_TERM_2_DTO,
-        fixture.TEST_TERM_3_DTO,
-        fixture.TEST_TERM_4_DTO,
-        fixture.TEST_TERM_5_DTO
-      ].map(term => ({
+      rows: [fixture.TEST_TERM_5_DTO, fixture.TEST_TERM_6_DTO].map(term => ({
         values: {
-          'code.value': term.getCode(),
-          'label.value': term.getLabel(),
-          'description.value': term.getDescription(),
-          'official.value': String(term.isOfficial())
+          'code.value': term.getCode()
         },
         selected: false
       })),
@@ -640,7 +726,7 @@ async function testValidateTerm() {
         pageSize: {
           value: 5
         },
-        range: '1-5 of 7'
+        range: '6-7 of 7'
       }
     },
     parameters: {
@@ -666,15 +752,6 @@ async function testValidateTerm() {
   form.expectJSON({
     grid: {
       rows: [
-        {
-          values: {
-            'code.value': fixture.TEST_TERM_6_DTO.getCode(),
-            'label.value': fixture.TEST_TERM_6_DTO.getLabel(),
-            'description.value': fixture.TEST_TERM_6_DTO.getDescription(),
-            'official.value': String(fixture.TEST_TERM_6_DTO.isOfficial())
-          },
-          selected: false
-        },
         {
           values: {
             'code.value': null,
@@ -683,13 +760,17 @@ async function testValidateTerm() {
             'official.value': String(true)
           },
           selected: true
-        }
+        },
+        { values: { 'code.value': fixture.TEST_TERM_1_DTO.getCode() } },
+        { values: { 'code.value': fixture.TEST_TERM_2_DTO.getCode() } },
+        { values: { 'code.value': fixture.TEST_TERM_3_DTO.getCode() } },
+        { values: { 'code.value': fixture.TEST_TERM_4_DTO.getCode() } }
       ],
       paging: {
         pageSize: {
           value: 5
         },
-        range: '6-7 of 7'
+        range: '1-5 of 7'
       }
     },
     parameters: {