diff --git a/packages/component-dashboard/src/components/withVersion.js b/packages/component-dashboard/src/components/withVersion.js
index bb5d8ba356aac51630b6851f6f09814ccd2a9b99..157573a025052c4688a2d0cc0e11cb8efc7aa51a 100644
--- a/packages/component-dashboard/src/components/withVersion.js
+++ b/packages/component-dashboard/src/components/withVersion.js
@@ -1,6 +1,6 @@
 import { compose } from 'recompose'
 import { connect } from 'react-redux'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import { ConnectPage } from 'xpub-connect'
 import { selectFragment } from 'xpub-selectors'
 
diff --git a/packages/component-dashboard/src/redux/conversion.js b/packages/component-dashboard/src/redux/conversion.js
index c15a21b1557a8bdadb98eab65dc90b74d534b0bb..85439c29d1440ef20372f6c8dd227fb422fc597e 100644
--- a/packages/component-dashboard/src/redux/conversion.js
+++ b/packages/component-dashboard/src/redux/conversion.js
@@ -1,5 +1,5 @@
 import { push } from 'react-router-redux'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import { ink as convertToHTML } from 'pubsweet-component-ink-frontend/actions'
 import uploadFile from 'xpub-upload'
 import { generateTitle, extractTitle } from '../lib/title'
@@ -54,10 +54,7 @@ export const uploadManuscript = acceptedFiles => dispatch => {
         const source = response.converted
         const title = extractTitle(source) || generateTitle(inputFile.name)
 
-        return dispatch(actions.createCollection({
-          collectionType: 'project',
-          title
-        })).then(({collection}) => {
+        return dispatch(actions.createCollection({ title })).then(({collection}) => {
           if (!collection.id) {
             throw new Error('Failed to create a project')
           }
diff --git a/packages/component-dashboard/src/redux/teams.js b/packages/component-dashboard/src/redux/teams.js
index baebcdca098559bc046ca7b38219f5f1da92b31b..aad6e89e33008615ef81ccf3f17dd66f1ca1b182 100644
--- a/packages/component-dashboard/src/redux/teams.js
+++ b/packages/component-dashboard/src/redux/teams.js
@@ -1,4 +1,4 @@
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 
 export const addUserToTeam = ({ team, teamTypeName, name, group, project, user }) => dispatch => {
   if (team) {
diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js
index 3b02e2eddba514aa905527fad7613a38773585bd..0afe8da511eb630365f14d8085cfc07984bd6631 100644
--- a/packages/component-manuscript/src/components/ManuscriptPage.js
+++ b/packages/component-manuscript/src/components/ManuscriptPage.js
@@ -1,24 +1,42 @@
-import { compose } from 'recompose'
+import { find } from 'lodash'
+import { compose, withHandlers } from 'recompose'
 import { connect } from 'react-redux'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import { ConnectPage } from 'xpub-connect'
-import { selectCurrentUser, selectCollection, selectFragment } from 'xpub-selectors'
+import { selectCurrentUser, selectCollection, selectFragment, selectVersion } from 'xpub-selectors'
 import Manuscript from './Manuscript'
 
 export default compose(
   ConnectPage(({ params }) => [
-    actions.getCollection({ id: params.project }),
-    actions.getFragment({ id: params.project }, { id: params.version })
+    actions.getCollection({ id: params.journal }),
+    actions.getFragment({ id: params.project }),
   ]),
   connect(
-    (state, ownProps) => ({
-      currentUser: selectCurrentUser(state),
-      project: selectCollection(state, ownProps.params.project),
-      version: selectFragment(state, ownProps.params.version)
-    }),
+    (state, { params }) => {
+      const currentUser = selectCurrentUser(state)
+      const journal = selectCollection(state, params.journal)
+      const project = selectFragment(state, params.project)
+      const version = selectVersion(project, params.version)
+
+      const content = version.manuscript.source // TODO: load from a file
+
+      return { currentUser, journal, project, version, content }
+    },
     {
       fileUpload: actions.fileUpload,
-      updateVersion: actions.updateFragment
     }
-  )
+  ),
+  withHandlers({
+    updateManuscript: ({ project, version, journal }) => data => {
+      version.manuscript = {
+        ...version.manuscript,
+        ...data
+      }
+
+      return actions.updateFragment(journal, {
+        id: project.id,
+        versions: project.versions
+      })
+    }
+  })
 )(Manuscript)
diff --git a/packages/component-review/src/components/DecisionPage.js b/packages/component-review/src/components/DecisionPage.js
index 74bdf0516a404bfe7edd25bc983830a050853aa0..3d96fb36cbb9273e0be501892077606cc63ad056 100644
--- a/packages/component-review/src/components/DecisionPage.js
+++ b/packages/component-review/src/components/DecisionPage.js
@@ -3,7 +3,7 @@ import { compose, withProps } from 'recompose'
 import { connect } from 'react-redux'
 import { push } from 'react-router-redux'
 import { reduxForm, SubmissionError } from 'redux-form'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import { ConnectPage } from 'xpub-connect'
 import { selectCollection, selectFragment } from 'xpub-selectors'
 import uploadFile from 'xpub-upload'
diff --git a/packages/component-review/src/components/ReviewPage.js b/packages/component-review/src/components/ReviewPage.js
index 2f9fe51636684bb316a41f68900379646b5b0ef1..82e665d9b56a3d562e35507c79c35704d61a4a45 100644
--- a/packages/component-review/src/components/ReviewPage.js
+++ b/packages/component-review/src/components/ReviewPage.js
@@ -3,11 +3,12 @@ import { compose, withProps } from 'recompose'
 import { connect } from 'react-redux'
 import { push } from 'react-router-redux'
 import { reduxForm, SubmissionError } from 'redux-form'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import { ConnectPage } from 'xpub-connect'
 import { selectCollection } from 'xpub-selectors'
 import uploadFile from 'xpub-upload'
 import ReviewLayout from './review/ReviewLayout'
+import { selectFragment } from '../../../xpub-selectors/src'
 
 const onSubmit = (values, dispatch, props) => {
   console.log('submit', values)
@@ -48,15 +49,12 @@ export default compose(
   connect(
     (state, ownProps) => {
       const project = selectCollection(state, ownProps.params.project)
-
-      const fragments = project.fragments.map(id => state.fragments[id])
+      const versions = project.fragments.map(id => state.fragments[id])
 
       return {
         project,
-        versions: filter(fragments, { fragmentType: 'version' }),
-        reviews: filter(fragments, { fragmentType: 'review' }),
+        versions,
         // version: selectFragment(state, ownProps.params.version),
-        // review: selectFragment(state, ownProps.params.review)
       }
     },
     {
diff --git a/packages/component-review/src/components/ReviewersPage.js b/packages/component-review/src/components/ReviewersPage.js
index 0cf76a9da745cab9406f933fd7f19b5dfec18b82..1f9e40b617c4675e220348b21b1102e0660fcc7e 100644
--- a/packages/component-review/src/components/ReviewersPage.js
+++ b/packages/component-review/src/components/ReviewersPage.js
@@ -1,12 +1,12 @@
-import { filter, find } from 'lodash'
+import { find } from 'lodash'
 import { compose, withProps } from 'recompose'
 import { connect } from 'react-redux'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import { ConnectPage } from 'xpub-connect'
 import { selectCollection, selectFragment } from 'xpub-selectors'
 import Reviewers from './reviewers/Reviewers'
 import ReviewerFormContainer from './reviewers/ReviewerFormContainer'
-import ReviewerContainer from './reviewers/ReviewerContainer'
+import ReviewContainer from './reviewers/ReviewContainer'
 
 export default compose(
   ConnectPage(({ params }) => [
@@ -21,30 +21,32 @@ export default compose(
       const project = selectCollection(state, ownProps.params.project)
       const version = selectFragment(state, ownProps.params.version)
 
-      const fragments = project.fragments.map(id => state.fragments[id])
-
-      const versions = filter(fragments, { fragmentType: 'version' })
-      const projectReviewers = filter(fragments, { fragmentType: 'projectReviewer' })
-      const reviewers = filter(fragments, { fragmentType: 'reviewer' })
+      const versions = project.fragments.map(id => state.fragments[id])
 
       const reviewerUsers = state.users.users
       // const reviewerUsers = filter(state.users.users, { reviewer: true })
 
       // populate the reviewer user
-      reviewers.forEach(reviewer => {
-        const projectReviewer = find(projectReviewers, { id: reviewer.projectReviewer })
+      versions.forEach(version => {
+        version.reviews.forEach(review => {
+          const reviewer = find(project.reviewers, {
+            id: review.reviewer
+          })
+
+          if (reviewer) {
+            reviewer._user = find(reviewerUsers, {
+              id: reviewer.user
+            })
 
-        if (projectReviewer) {
-          reviewer._user = find(reviewerUsers, { id: projectReviewer.user })
-        }
+            review._reviewer = reviewer
+          }
+        })
       })
 
       return {
         project,
         version,
         versions,
-        projectReviewers,
-        reviewers,
         reviewerUsers,
         // teams: state.teams,
       }
@@ -52,6 +54,6 @@ export default compose(
   ),
   withProps({
     ReviewerForm: ReviewerFormContainer,
-    Reviewer: ReviewerContainer
+    Review: ReviewContainer
   })
 )(Reviewers)
diff --git a/packages/component-review/src/components/reviewers/ReviewerFormContainer.js b/packages/component-review/src/components/reviewers/ReviewerFormContainer.js
index ca2743ee33ec5a70f5f57da7f0e2053a7d2695ae..8ee41eec553ddde06de7c500d7590f8d6f7d00dc 100644
--- a/packages/component-review/src/components/reviewers/ReviewerFormContainer.js
+++ b/packages/component-review/src/components/reviewers/ReviewerFormContainer.js
@@ -2,50 +2,56 @@ import { find, some } from 'lodash'
 import { compose, withHandlers } from 'recompose'
 import { reduxForm, SubmissionError } from 'redux-form'
 import { connect } from 'react-redux'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import ReviewerForm from './ReviewerForm'
 
-const getProjectReviewer = (props, user) => {
-  const projectReviewer = find(props.projectReviewers, { user: user.id })
+const getReviewer = (props, user) => {
+  const reviewer = find(props.project.reviewers, { user: user.id })
 
-  return projectReviewer ? Promise.resolve(projectReviewer) : addProjectReviewer(props, user)
+  return reviewer ? Promise.resolve(reviewer) : addReviewer(props, user)
 }
 
-const addProjectReviewer = (props, user) => {
-  return props.createFragment(props.project, {
-    fragmentType: 'projectReviewer',
-    user: user.id,
-  }).then(result => result.fragment)
+const addReviewer = (props, user) => {
+  const reviewer = {
+    user: user.id
+  }
+
+  return props.updateProject(props.project, {
+    reviewers: [].concat(props.project.reviewers, reviewer)
+  }).then(() => reviewer)
 }
 
-const addReviewer = (props, projectReviewer) => {
-  return props.createFragment(props.project, {
-    fragmentType: 'reviewer',
-    parentVersion: props.version.id,
-    projectReviewer: projectReviewer.id,
+const addInvitation = (props, reviewer) => {
+  const invitation = {
+    reviewer: reviewer.id,
     status: 'invited',
     events: {
       invited: new Date()
     }
-  })
+  }
+
+  return props.updateVersion(props.project, {
+    id: props.version.id,
+    invitations: [].concat(props.version.invitations, invitation)
+  }).then(() => invitation)
 }
 
 const handleSubmit = props => reset => values => {
   // TODO: create a user account if values.user.id is null
 
-  return getProjectReviewer(props, values.user).then(projectReviewer => {
-    if (some(props.reviewers, { projectReviewer: projectReviewer.id })) {
+  return getReviewer(props, values.user).then(reviewer => {
+    if (some(props.version.invitations, { reviewer: reviewer.id })) {
       throw new SubmissionError('This reviewer has already been added')
     }
 
-    return addReviewer(props, projectReviewer)
+    return addInvitation(props, reviewer)
   }).then(() => reset())
 }
 
 const loadOptions = props => input => {
   const options = props.reviewerUsers
 
-  // TODO: put existing, uninvited projectReviewers at the top
+  // TODO: put existing, uninvited project reviewers at the top
 
   // TODO: filter users based on input
 
@@ -54,7 +60,8 @@ const loadOptions = props => input => {
 
 export default compose(
   connect(null, {
-    createFragment: actions.createFragment,
+    updateProject: actions.updateCollection,
+    updateVersion: actions.updateFragment,
   }),
   withHandlers({
     loadOptions: props => loadOptions(props),
diff --git a/packages/component-submit/src/components/SubmitPage.js b/packages/component-submit/src/components/SubmitPage.js
index da1ccd9aa4cd1298d4be843db5c5aec584109f13..4b9cb859c5bccf5be972ed3a4c3ff771c7f31c05 100644
--- a/packages/component-submit/src/components/SubmitPage.js
+++ b/packages/component-submit/src/components/SubmitPage.js
@@ -1,28 +1,26 @@
-import { pick } from 'lodash'
+import { find, pick } from 'lodash'
 import { compose, withProps, withState, withHandlers } from 'recompose'
 import { connect } from 'react-redux'
 import { push } from 'react-router-redux'
 import { reduxForm, SubmissionError } from 'redux-form'
-import actions from 'pubsweet-client/src/actions'
+import { actions } from 'pubsweet-client'
 import uploadFile from 'xpub-upload'
 import { ConnectPage } from 'xpub-connect'
-import { selectCollection, selectFragment } from 'xpub-selectors'
+import { selectCollection, selectFragment, selectVersion } from 'xpub-selectors'
 import Submit from './Submit'
 
-const onSubmit = (values, dispatch, props) => {
+const onSubmit = (values, dispatch, { journal, project, version }) => {
   console.log('submit', values)
 
-  return dispatch(actions.updateFragment(props.project, {
-    id: props.version.id,
-    submitted: new Date(),
-    ...values
+  Object.assign(version, values, {
+    submitted: new Date()
+  })
+
+  return dispatch(actions.updateFragment(journal, {
+    id: project.id,
+    versions: project.versions
   })).then(() => {
-    return dispatch(actions.updateCollection({
-      id: props.project.id,
-      status: 'submitted'
-    }))
-  }).then(() => {
-    dispatch(push(`/`))
+    dispatch(push('/'))
   }).catch(error => {
     if (error.validationErrors) {
       throw new SubmissionError()
@@ -31,13 +29,16 @@ const onSubmit = (values, dispatch, props) => {
 }
 
 // TODO: redux-form doesn't have an onBlur handler(?)
-const onBlur = (values, dispatch, props) => {
-  console.log('change', values)
+const onBlur = (values, dispatch, { journal, project, version }) => {
+  console.log('blur', values)
 
-  return dispatch(actions.updateFragment(props.project, {
-    id: props.version.id,
-    // submitted: false,
-    ...values
+  Object.assign(version, values, {
+    submitted: new Date()
+  })
+
+  return dispatch(actions.updateFragment(journal, {
+    id: project.id,
+    versions: project.versions
   }))
 
   // TODO: display a notification when saving/saving completes/saving fails
@@ -45,14 +46,17 @@ const onBlur = (values, dispatch, props) => {
 
 export default compose(
   ConnectPage(({ params }) => [
-    actions.getCollection({ id: params.project }),
-    actions.getFragment({ id: params.project }, { id: params.version })
+    actions.getCollection({ id: params.journal }),
+    actions.getFragment({ id: params.project })
   ]),
   connect(
-    (state, ownProps) => ({
-      project: selectCollection(state, ownProps.params.project),
-      version: selectFragment(state, ownProps.params.version)
-    }),
+    (state, { params }) => {
+      const journal = selectCollection(state, params.journal)
+      const project = selectFragment(state, params.project)
+      const version = selectVersion(project, params.version)
+
+      return { journal, project, version }
+    },
     {
       uploadFile
     }