diff --git a/src/mol-plugin/behavior/behavior.ts b/src/mol-plugin/behavior/behavior.ts
index 1064ac2ad5b21afa14c16ec4444543a5e198becc..39123238e1bd546a6d661fefe8bd5fb9eaa2330b 100644
--- a/src/mol-plugin/behavior/behavior.ts
+++ b/src/mol-plugin/behavior/behavior.ts
@@ -31,11 +31,16 @@ namespace PluginBehavior {
         name: string,
         ctor: Ctor<P>,
         label?: (params: P) => { label: string, description?: string },
-        display: { name: string, description?: string },
-        params?: Transformer.Definition<Root, Behavior, P>['params']
+        display: {
+            name: string,
+            group: string,
+            description?: string
+        },
+        params?: Transformer.Definition<Root, Behavior, P>['params'],
     }
 
     export function create<P>(params: CreateParams<P>) {
+        // TODO: cache groups etc
         return PluginStateTransform.Create<Root, Behavior, P>({
             name: params.name,
             display: params.display,
diff --git a/src/mol-plugin/behavior/built-in/data.ts b/src/mol-plugin/behavior/built-in/data.ts
index d9e6a6b745ee2ffec6d8996deedf4be2d548309d..e8107d0fc29dc7cfbb40a5a385586475608184b7 100644
--- a/src/mol-plugin/behavior/built-in/data.ts
+++ b/src/mol-plugin/behavior/built-in/data.ts
@@ -11,13 +11,13 @@ import { StateTree } from 'mol-state';
 export const SetCurrentObject = PluginBehavior.create({
     name: 'set-current-data-object-behavior',
     ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.SetCurrentObject, ({ ref }, ctx) => ctx.state.data.setCurrent(ref)),
-    display: { name: 'Set Current Handler' }
+    display: { name: 'Set Current Handler', group: 'Data' }
 });
 
 export const Update = PluginBehavior.create({
     name: 'update-data-behavior',
     ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.Update, ({ tree }, ctx) => ctx.state.updateData(tree)),
-    display: { name: 'Update Data Handler' }
+    display: { name: 'Update Data Handler', group: 'Data' }
 });
 
 export const RemoveObject = PluginBehavior.create({
@@ -26,5 +26,5 @@ export const RemoveObject = PluginBehavior.create({
         const tree = StateTree.build(ctx.state.data.tree).delete(ref).getTree();
         ctx.state.updateData(tree);
     }),
-    display: { name: 'Remove Object Handler' }
+    display: { name: 'Remove Object Handler', group: 'Data' }
 });
\ No newline at end of file
diff --git a/src/mol-plugin/behavior/built-in/representation.ts b/src/mol-plugin/behavior/built-in/representation.ts
index 27c0d14348dbf6a9a5e5d8fd47a9e361f7ea0676..785acc9a4eb5048e63dd5576352648d8381a6a91 100644
--- a/src/mol-plugin/behavior/built-in/representation.ts
+++ b/src/mol-plugin/behavior/built-in/representation.ts
@@ -44,7 +44,7 @@ export const AddRepresentationToCanvas = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Add Representation To Canvas' }
+    display: { name: 'Add Representation To Canvas', group: 'Data' }
 });
 
 export const HighlightLoci = PluginBehavior.create({
@@ -64,7 +64,7 @@ export const HighlightLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Highlight Loci on Canvas' }
+    display: { name: 'Highlight Loci on Canvas', group: 'Data' }
 });
 
 export const SelectLoci = PluginBehavior.create({
@@ -77,5 +77,5 @@ export const SelectLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Select Loci on Canvas' }
+    display: { name: 'Select Loci on Canvas', group: 'Data' }
 });
\ No newline at end of file
diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx
index eecfb2dd2366aa1105885c9119ebe1f2e1e64905..ec6155d9fe7225f353060490035bd4e82a64416f 100644
--- a/src/mol-plugin/ui/controls.tsx
+++ b/src/mol-plugin/ui/controls.tsx
@@ -6,7 +6,7 @@
 
 import * as React from 'react';
 import { PluginContext } from '../context';
-import { Transform, Transformer, StateObject } from 'mol-state';
+import { Transform, Transformer } from 'mol-state';
 import { ParametersComponent } from 'mol-app/component/parameters';
 
 export class Controls extends React.Component<{ plugin: PluginContext }, { id: string }> {
@@ -73,7 +73,7 @@ export class _test_CreateTransform extends React.Component<{ plugin: PluginConte
 
     render() {
         const obj = this.getObj();
-        if (obj.state !== StateObject.StateType.Ok) {
+        if (obj.status !== 'ok') {
             // TODO filter this elsewhere
             return <div />;
         }
diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state-tree.tsx
index 01c9509152f75d1995d9905a38acb8d0fd06d5c4..8c7dc5dd0e36ea35a872c9586b93ad0c1a47791c 100644
--- a/src/mol-plugin/ui/state-tree.tsx
+++ b/src/mol-plugin/ui/state-tree.tsx
@@ -7,7 +7,7 @@
 import * as React from 'react';
 import { PluginContext } from '../context';
 import { PluginStateObject } from 'mol-plugin/state/base';
-import { StateObject, State } from 'mol-state'
+import { State } from 'mol-state'
 import { PluginCommands } from 'mol-plugin/command';
 
 export class StateTree extends React.Component<{ plugin: PluginContext, state: State }, { }> {
@@ -35,9 +35,9 @@ export class StateTreeNode extends React.Component<{ plugin: PluginContext, node
             PluginCommands.Data.RemoveObject.dispatch(this.props.plugin, { ref: this.props.nodeRef });
         }}>X</a>]</>
 
-        if (!obj.obj) {
+        if (obj.status !== 'ok' || !obj.obj) {
             return <div style={{ borderLeft: '1px solid black', paddingLeft: '7px' }}>
-                {remove} {StateObject.StateType[obj.state]} {obj.errorText}
+                {remove} {obj.status} {obj.errorText}
             </div>;
         }
         const props = obj.obj!.props as PluginStateObject.Props;
diff --git a/src/mol-state/util/immutable-tree.ts b/src/mol-state/immutable-tree.ts
similarity index 100%
rename from src/mol-state/util/immutable-tree.ts
rename to src/mol-state/immutable-tree.ts
diff --git a/src/mol-state/manager.ts b/src/mol-state/manager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0042b15a93f4184628d575672d2570f63ac64dc6
--- /dev/null
+++ b/src/mol-state/manager.ts
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+// TODO manage snapshots etc
\ No newline at end of file
diff --git a/src/mol-state/object.ts b/src/mol-state/object.ts
index dddfcb68b41229e7a54a39d3bbe0a4479d6fe887..ed74ec0927f263fec6efe20aa313b5e464f137a6 100644
--- a/src/mol-state/object.ts
+++ b/src/mol-state/object.ts
@@ -1,33 +1,22 @@
-
 /**
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Transform } from './transform';
 import { UUID } from 'mol-util';
+import { Transform } from './transform';
 
-/** A mutable state object */
-export interface StateObject<P = any, D = any> {
+export { StateObject, StateObjectBox }
+
+interface StateObject<P = any, D = any, Type = { }> {
     readonly id: UUID,
     readonly type: StateObject.Type,
     readonly props: P,
     readonly data: D
 }
 
-export namespace StateObject {
-    export enum StateType {
-        // The object has been successfully created
-        Ok,
-        // An error occured during the creation of the object
-        Error,
-        // The object is queued to be created
-        Pending,
-        // The object is currently being created
-        Processing
-    }
-
+namespace StateObject {
     export interface Type<Info = any> {
         info: Info
     }
@@ -40,7 +29,7 @@ export namespace StateObject {
 
     export function create<Props, Data, TypeInfo>(typeInfo: TypeInfo) {
         const dataType: Type<TypeInfo> = { info: typeInfo };
-        return class implements StateObject<Props, Data> {
+        return class implements StateObject<Props, Data, Type<TypeInfo>> {
             static type = dataType;
             static is(obj?: StateObject): obj is StateObject<Props, Data> { return !!obj && dataType === obj.type; }
             id = UUID.create();
@@ -48,13 +37,25 @@ export namespace StateObject {
             constructor(public props: Props, public data: Data) { }
         }
     }
+}
+
+interface StateObjectBox {
+    ref: Transform.Ref,
+    props: unknown,
+
+    status: StateObjectBox.Status,
+    errorText?: string,
+    obj?: StateObject,
+    version: string
+}
+
+namespace StateObjectBox {
+    export type Status = 'ok' | 'error' | 'pending' | 'processing'
 
-    export interface Node {
-        ref: Transform.Ref,
-        state: StateType,
-        props: unknown,
-        errorText?: string,
-        obj?: StateObject,
-        version: string
+    export interface Props {
+        isVisible: boolean,
+        isHidden: boolean,
+        isBound: boolean,
+        isExpanded: boolean
     }
 }
\ No newline at end of file
diff --git a/src/mol-state/selection.ts b/src/mol-state/selection.ts
index 60c512855f7ab9070b5f25d93b43b70594464545..560a0ef4ca2c20ae6257bbeeb4107f1eb9de0ed9 100644
--- a/src/mol-state/selection.ts
+++ b/src/mol-state/selection.ts
@@ -4,13 +4,13 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { StateObject } from './object';
+import { StateObject, StateObjectBox } from './object';
 import { State } from './state';
-import { ImmutableTree } from './util/immutable-tree';
+import { ImmutableTree } from './immutable-tree';
 
 namespace StateSelection {
-    export type Selector = Query | Builder | string | StateObject.Node;
-    export type NodeSeq = StateObject.Node[]
+    export type Selector = Query | Builder | string | StateObjectBox;
+    export type NodeSeq = StateObjectBox[]
     export type Query = (state: State) => NodeSeq;
 
     export function select(s: Selector, state: State) {
@@ -27,8 +27,8 @@ namespace StateSelection {
         return query;
     }
 
-    function isObj(arg: any): arg is StateObject.Node {
-        return (arg as StateObject.Node).version !== void 0;
+    function isObj(arg: any): arg is StateObjectBox {
+        return (arg as StateObjectBox).version !== void 0;
     }
 
     function isBuilder(arg: any): arg is Builder {
@@ -40,13 +40,13 @@ namespace StateSelection {
     }
 
     export interface Builder {
-        flatMap(f: (n: StateObject.Node) => StateObject.Node[]): Builder;
-        mapEntity(f: (n: StateObject.Node) => StateObject.Node): Builder;
+        flatMap(f: (n: StateObjectBox) => StateObjectBox[]): Builder;
+        mapEntity(f: (n: StateObjectBox) => StateObjectBox): Builder;
         unique(): Builder;
 
         parent(): Builder;
         first(): Builder;
-        filter(p: (n: StateObject.Node) => boolean): Builder;
+        filter(p: (n: StateObjectBox) => boolean): Builder;
         subtree(): Builder;
         children(): Builder;
         ofType(t: StateObject.Type): Builder;
@@ -68,7 +68,7 @@ namespace StateSelection {
 
     export function byRef(...refs: string[]) {
         return build(() => (state: State) => {
-            const ret: StateObject.Node[] = [];
+            const ret: StateObjectBox[] = [];
             for (const ref of refs) {
                 const n = state.objects.get(ref);
                 if (!n) continue;
@@ -78,13 +78,13 @@ namespace StateSelection {
         });
     }
 
-    export function byValue(...objects: StateObject.Node[]) { return build(() => (state: State) => objects); }
+    export function byValue(...objects: StateObjectBox[]) { return build(() => (state: State) => objects); }
 
     registerModifier('flatMap', flatMap);
-    export function flatMap(b: Selector, f: (obj: StateObject.Node, state: State) => NodeSeq) {
+    export function flatMap(b: Selector, f: (obj: StateObjectBox, state: State) => NodeSeq) {
         const q = compile(b);
         return build(() => (state: State) => {
-            const ret: StateObject.Node[] = [];
+            const ret: StateObjectBox[] = [];
             for (const n of q(state)) {
                 for (const m of f(n, state)) {
                     ret.push(m);
@@ -95,10 +95,10 @@ namespace StateSelection {
     }
 
     registerModifier('mapEntity', mapEntity);
-    export function mapEntity(b: Selector, f: (n: StateObject.Node, state: State) => StateObject.Node | undefined) {
+    export function mapEntity(b: Selector, f: (n: StateObjectBox, state: State) => StateObjectBox | undefined) {
         const q = compile(b);
         return build(() => (state: State) => {
-            const ret: StateObject.Node[] = [];
+            const ret: StateObjectBox[] = [];
             for (const n of q(state)) {
                 const x = f(n, state);
                 if (x) ret.push(x);
@@ -112,7 +112,7 @@ namespace StateSelection {
         const q = compile(b);
         return build(() => (state: State) => {
             const set = new Set<string>();
-            const ret: StateObject.Node[] = [];
+            const ret: StateObjectBox[] = [];
             for (const n of q(state)) {
                 if (!set.has(n.ref)) {
                     set.add(n.ref);
@@ -133,7 +133,7 @@ namespace StateSelection {
     }
 
     registerModifier('filter', filter);
-    export function filter(b: Selector, p: (n: StateObject.Node) => boolean) { return flatMap(b, n => p(n) ? [n] : []); }
+    export function filter(b: Selector, p: (n: StateObjectBox) => boolean) { return flatMap(b, n => p(n) ? [n] : []); }
 
     registerModifier('subtree', subtree);
     export function subtree(b: Selector) {
@@ -147,7 +147,7 @@ namespace StateSelection {
     registerModifier('children', children);
     export function children(b: Selector) {
         return flatMap(b, (n, s) => {
-            const nodes: StateObject.Node[] = [];
+            const nodes: StateObjectBox[] = [];
             s.tree.nodes.get(n.ref)!.children.forEach(c => nodes.push(s.objects.get(c!)!));
             return nodes;
         });
@@ -162,7 +162,7 @@ namespace StateSelection {
     registerModifier('parent', parent);
     export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.objects.get(s.tree.nodes.get(n.ref)!.parent))); }
 
-    function findAncestorOfType({ tree, objects }: State, root: string, type: StateObject.Type): StateObject.Node | undefined {
+    function findAncestorOfType({ tree, objects }: State, root: string, type: StateObject.Type): StateObjectBox | undefined {
         let current = tree.nodes.get(root)!;
         while (true) {
             current = tree.nodes.get(current.parent)!;
diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts
index 149486e64e48613a24002e0a3aff010ee6ac01b7..aecbbcb655199e861f70702faaf3750677bdaef7 100644
--- a/src/mol-state/state.ts
+++ b/src/mol-state/state.ts
@@ -4,10 +4,10 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { StateObject } from './object';
+import { StateObject, StateObjectBox } from './object';
 import { StateTree } from './tree';
 import { Transform } from './transform';
-import { ImmutableTree } from './util/immutable-tree';
+import { ImmutableTree } from './immutable-tree';
 import { Transformer } from './transformer';
 import { StateContext } from './context';
 import { UUID } from 'mol-util';
@@ -87,7 +87,7 @@ class State {
         this.objects.set(tree.rootRef, {
             ref: tree.rootRef,
             obj: rootObject,
-            state: StateObject.StateType.Ok,
+            status: 'ok',
             version: root.version,
             props: { ...defaultObjectProps }
         });
@@ -101,7 +101,7 @@ class State {
 }
 
 namespace State {
-    export type Objects = Map<Transform.Ref, StateObject.Node>
+    export type Objects = Map<Transform.Ref, StateObjectBox>
 
     export interface Snapshot {
         readonly tree: StateTree.Serialized,
@@ -177,15 +177,15 @@ namespace State {
         return deletes;
     }
 
-    function setObjectState(ctx: UpdateContext, ref: Ref, state: StateObject.StateType, errorText?: string) {
+    function setObjectState(ctx: UpdateContext, ref: Ref, status: StateObjectBox.Status, errorText?: string) {
         let changed = false;
         if (ctx.objects.has(ref)) {
             const obj = ctx.objects.get(ref)!;
-            changed = obj.state !== state;
-            obj.state = state;
+            changed = obj.status !== status;
+            obj.status = status;
             obj.errorText = errorText;
         } else {
-            const obj: StateObject.Node = { ref, state, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } };
+            const obj: StateObjectBox = { ref, status, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } };
             ctx.objects.set(ref, obj);
             changed = true;
         }
@@ -193,7 +193,7 @@ namespace State {
     }
 
     function _initVisitor(t: ImmutableTree.Node<Transform>, _: any, ctx: UpdateContext) {
-        setObjectState(ctx, t.ref, StateObject.StateType.Pending);
+        setObjectState(ctx, t.ref, 'pending');
     }
     /** Return "resolve set" */
     function initObjectState(ctx: UpdateContext, roots: Ref[]) {
@@ -203,7 +203,7 @@ namespace State {
     }
 
     function doError(ctx: UpdateContext, ref: Ref, errorText: string) {
-        setObjectState(ctx, ref, StateObject.StateType.Error, errorText);
+        setObjectState(ctx, ref, 'error', errorText);
         const wrap = ctx.objects.get(ref)!;
         if (wrap.obj) {
             ctx.stateCtx.events.object.removed.next({ ref });
@@ -232,11 +232,11 @@ namespace State {
     }
 
     async function updateSubtree(ctx: UpdateContext, root: Ref) {
-        setObjectState(ctx, root, StateObject.StateType.Processing);
+        setObjectState(ctx, root, 'processing');
 
         try {
             const update = await updateNode(ctx, root);
-            setObjectState(ctx, root, StateObject.StateType.Ok);
+            setObjectState(ctx, root, 'ok');
             if (update.action === 'created') {
                 ctx.stateCtx.events.object.created.next({ ref: root, obj: update.obj! });
             } else if (update.action === 'updated') {
@@ -268,7 +268,7 @@ namespace State {
             objects.set(currentRef, {
                 ref: currentRef,
                 obj,
-                state: StateObject.StateType.Ok,
+                status: 'ok',
                 version: transform.version,
                 props: { ...ctx.stateCtx.defaultObjectProps, ...transform.defaultProps }
             });
@@ -283,7 +283,7 @@ namespace State {
                     objects.set(currentRef, {
                         ref: currentRef,
                         obj,
-                        state: StateObject.StateType.Ok,
+                        status: 'ok',
                         version: transform.version,
                         props: { ...ctx.stateCtx.defaultObjectProps, ...current.props, ...transform.defaultProps }
                     });
diff --git a/src/mol-state/transformer.ts b/src/mol-state/transformer.ts
index d8727cfc92995351a3240959bedee94a8d87de17..f58771db7f445eb6524acbb892c94aee27acf241 100644
--- a/src/mol-state/transformer.ts
+++ b/src/mol-state/transformer.ts
@@ -59,7 +59,7 @@ export namespace Transformer {
          */
         update?(params: UpdateParams<A, B, P>, globalCtx: unknown): Task<UpdateResult> | UpdateResult,
 
-        params?: {
+        readonly params?: {
             /** Check the parameters and return a list of errors if the are not valid. */
             default?(a: A, globalCtx: unknown): P,
             /** Specify default control descriptors for the parameters */
@@ -68,7 +68,7 @@ export namespace Transformer {
             validate?(a: A, params: P, globalCtx: unknown): string[] | undefined,
             /** Optional custom parameter equality. Use deep structural equal by default. */
             areEqual?(oldParams: P, newParams: P): boolean
-        }
+        },
 
         /** Test if the transform can be applied to a given node */
         isApplicable?(a: A, globalCtx: unknown): boolean,
@@ -77,7 +77,7 @@ export namespace Transformer {
         isSerializable?(params: P): { isSerializable: true } | { isSerializable: false; reason: string },
 
         /** Custom conversion to and from JSON */
-        customSerialization?: { toJSON(params: P, obj?: B): any, fromJSON(data: any): P }
+        readonly customSerialization?: { toJSON(params: P, obj?: B): any, fromJSON(data: any): P }
     }
 
     const registry = new Map<Id, Transformer<any, any>>();
diff --git a/src/mol-state/tree.ts b/src/mol-state/tree.ts
index f7fbd4955dae802b5bf23fb5535ba8370cd03a7b..d26bf4902e56bfeddc605d241f554965fd42a75d 100644
--- a/src/mol-state/tree.ts
+++ b/src/mol-state/tree.ts
@@ -5,7 +5,7 @@
  */
 
 import { Transform } from './transform';
-import { ImmutableTree } from './util/immutable-tree';
+import { ImmutableTree } from './immutable-tree';
 import { Transformer } from './transformer';
 import { StateObject } from './object';
 
diff --git a/src/perf-tests/state.ts b/src/perf-tests/state.ts
index dd594d26c88e747ed8c30b3fa97db41986965ad3..dbe511f90b081a196d0c1fb2f4a729560bcbe8d0 100644
--- a/src/perf-tests/state.ts
+++ b/src/perf-tests/state.ts
@@ -68,8 +68,7 @@ function hookEvents(state: State) {
     state.context.events.object.created.subscribe(e => console.log('created:', e.ref));
     state.context.events.object.removed.subscribe(e => console.log('removed:', e.ref));
     state.context.events.object.replaced.subscribe(e => console.log('replaced:', e.ref));
-    state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref,
-        StateObject.StateType[state.objects.get(e.ref)!.state]));
+    state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.objects.get(e.ref)!.status));
     state.context.events.object.updated.subscribe(e => console.log('updated:', e.ref));
 }