We’ll be looking at the codesandbox-client repository (Github link here if you want to follow along in your own code editor). On first impressions, it’s a quite large JavaScript repository. lerna is used to split the codebase into manageable smaller sections.
For this teardown series, we’re going to look at the app
package (located in the packages/app folder). The app
package contains the full CodeSandbox app as well as the embedded app you might have seen on Medium or other websites. It's written in React 16.5.2.
The repository commit hash at the time of writing is 450afd6
.
Good user feedback is important because it helps us developers prioritise the next feature to build or bug to fix. Getting feedback could be as simple as sitting with users while they use your app, but building feedback submission channels into your app means you’ll get a stream of bug reports and feature requests without spending time interviewing people.
Airtable is really good for collecting feedback.
If you haven’t used it before, it’s kind of like Microsoft Excel or Google Sheets, but on steroids. It feels a little bit less like a spreadsheet app and more like a proper database.
It’s good for feedback because it’s built for less technical-minded people. Your product manager or UI designer can easily access the data, as opposed to something like, say, sending feedback to MongoDB, where you might need to help them out.
This is how CodeSandbox collects feedback from users:
Above: me sharing how much I love CodeSandbox ❤️
Once you hit submit, a new entry in CodeSandbox’s Airtable is generated with your feedback as well as some relevant user information. In the next section, I’ll run through how it’s built, and how you can use it in your own apps.
There are three separate files responsible for Airtable functionality.
setAirtable.js sets up the Airtable JavaScript API with a specified API key. This is nice because it encapsulates the API key in a single place within the repository.
// packages/app/src/app/store/utils/setAirtable.js
import Airtable from 'airtable';
export default new Airtable({ apiKey: '<YOUR_AIRTABLE_API_KEY>' });
pushToAirtable.js connects to the Airtable base (their term for a particular spreadsheet), and wraps the Airtable 'create record' function with application-specific arguments relating to our feedback. It also handles errors, logging them to the console. Note that the function returns a Promise, which was chosen so that the function could possibly be called in a promise chain.
// packages/app/src/app/store/utils/pushToAirtable.js
import Airtable from './setAirtable';
const base = Airtable.base('<YOUR_AIRTABLE_BASE_KEY>');
export default ({ feedback, emoji, sandboxId, username, email }) => new Promise((resolve, reject) => { base('feedback').create( { feedback, emoji, sandboxId, username, email, }, err => { if (err) { console.error(err); reject(); }
resolve(); } ); });
Feedback.js is the React feedback modal component which calls pushToAirtable()
. I also like the way that props
and user
are destructured inside sendFeedback()
.
// packages/app/src/app/pages/common/Modals/FeedbackModal/Feedback.js// Some code has been omitted for brevity
...
import pushToAirtable from 'app/store/utils/pushToAirtable';
function sendFeedback(props) { const { feedback, emoji, sandboxId, user } = props; const { username, email } = user || {};
return pushToAirtable({ feedback, emoji, sandboxId, username, email });}
class Feedback extends React.Component { ... onSubmit = evt => { const { id, user, signals } = this.props; const { feedback, emoji } = this.state; evt.preventDefault();
this.setState({ loading: true }, () => { sendFeedback({ sandboxId: id, feedback, emoji, user, }) ... }; ... render() { return ( <form onSubmit={this.onSubmit}> ... </form> ); }}
So really, there’s not that much to it. It’s as simple as:
setAirtable.js
)pushToAirtable.js
)onSubmit()
for forms or onClick()
for buttons inside your React component (Feedback.js
)And why stop at collecting just user feedback with Airtable? You could expand on this method to send other kinds of data too, such as particular application events or logs. I’d love to hear about how you use it in your own projects.
In the next article, we’ll take a tip from CodeSandbox in handling Do Not Track settings from your users.
Follow me on Twitter — I’ll be posting future articles there as I write them.
Have you read through the CodeSandbox source code yourself? If you learnt anything interesting from it, please let me know in the comments 🙌