├── .gitignore
├── README.md
├── package.json
├── src
└── index.tsx
├── test
└── index.test.tsx
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | .rts2_cache_cjs
6 | .rts2_cache_esm
7 | .rts2_cache_umd
8 | dist
9 | yarn.lock
10 | package-lock.json
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @oieduardorabelo/use-user-agent
2 |
3 | React Hooks to detect browsers user-agent using [`ua-parser-js`](https://github.com/faisalman/ua-parser-js) as main dependency.
4 |
5 | To install it:
6 |
7 | ```
8 | yarn add @oieduardorabelo/use-user-agent
9 | ```
10 |
11 | ## Example
12 |
13 | An online demo is available at CodeSandbox:
14 |
15 | - **Live demo:** https://codesandbox.io/s/live-demo-use-user-agent-d7iyg
16 |
17 | If you've any issues, **open an issue with a CodeSandbox link** with your issue
18 |
19 | ## API Explained
20 |
21 | In your app, you can add:
22 |
23 | ```javascript
24 | import { useUserAgent } from '@oieduardorabelo/use-user-agent';
25 |
26 | function App() {
27 | let details = useUserAgent(uastring)
28 | ...
29 | }
30 | ```
31 |
32 | ### `details` object is composed of:
33 |
34 | - `details`: It is either `null` or an ua-parser-js object.
35 | - `details.os`: It is a `Object`, with keys `name` and `version` as `string|undefined`
36 | - `details.browser`: It is a `Object`, with keys `name`, `version` and `major` as `string|undefined`
37 | - `details.cpu`: It is a `Object`, with keys `architecture` as `string|undefined`
38 | - `details.device`: It is a `Object`, with keys `vendor`, `model` and `type` as `string|undefined`
39 | - `details.engine`: It is a `Object`, with keys `name` and `version` as `string|undefined`
40 |
41 | For full documentation, refer to [ua-parser-js repository](https://github.com/faisalman/ua-parser-js#example).
42 |
43 | ### `uastring` parameter is composed of:
44 |
45 | - `uastring`: It is a `String`, should be a user-agent string, if none is passed, we default to `window.navigator.userAgent`
46 |
47 | ## Examples
48 |
49 | Using default value from `useUserAgent()`:
50 |
51 | ```javascript
52 | import { useUserAgent } from '@oieduardorabelo/use-user-agent';
53 |
54 | function App() {
55 | let details = useUserAgent(); // default is `window.navigator.userAgent`
56 |
57 | if (!details) {
58 | return null;
59 | }
60 |
61 | let { os, browser, cpu, device, engine } = details;
62 |
63 | return (
64 |
65 |
My OS is {os.name}, on version {os.version}
66 |
My Browser is {browser.name}, on version {browser.version} with major {browser.major}
67 |
My CPU architecture is {cpu.architecture}
68 |
My Device is {device.vendor}, with model {device.model} of type {device.type}
69 |
My Engine is {engine.name} with version {engine.version}
70 |
71 | );
72 | }
73 | ```
74 |
75 | Passing a custom user-agent string:
76 |
77 | ```javascript
78 | import { useUserAgent } from '@oieduardorabelo/use-user-agent';
79 |
80 | function App() {
81 | let uastring = "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.11 (KHTML, like Gecko) Version/7.1.0.7 Safari/534.11";
82 | let details = useUserAgent(uastring);
83 |
84 | if (!details) {
85 | return null;
86 | }
87 |
88 | let { os, browser, cpu, device, engine } = details;
89 |
90 | return (
91 |
92 |
My OS is {os.name}, on version {os.version}
93 |
My Browser is {browser.name}, on version {browser.version} with major {browser.major}
94 |
My CPU architecture is {cpu.architecture}
95 |
My Device is {device.vendor}, with model {device.model} of type {device.type}
96 |
My Engine is {engine.name} with version {engine.version}
97 |
98 | );
99 | }
100 | ```
101 |
102 | ### License
103 |
104 | [MIT License](https://oss.ninja/mit/oieduardorabelo/) © [Eduardo Rabelo](https://eduardorabelo.me)
105 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@oieduardorabelo/use-user-agent",
3 | "description": "React Hooks to detect browsers user-agent using ua-parser-js as main dependency.",
4 | "license": "MIT",
5 | "author": "Eduardo Rabelo (https://github.com/oieduardorabelo)",
6 | "repository": {
7 | "url": "https://github.com/oieduardorabelo/use-user-agent",
8 | "type": "git"
9 | },
10 | "version": "5.0.1",
11 | "main": "dist/index.js",
12 | "module": "dist/use-user-agent.esm.js",
13 | "files": [
14 | "dist"
15 | ],
16 | "scripts": {
17 | "build": "tsdx build",
18 | "fmt:p": "prettier-package-json --write",
19 | "start": "tsdx watch",
20 | "test": "tsdx test --env=jsdom"
21 | },
22 | "typings": "dist/index.d.ts",
23 | "dependencies": {
24 | "ua-parser-js": "1.0.2"
25 | },
26 | "peerDependencies": {
27 | "react": "16.8 - 18",
28 | "react-dom": "16.8 - 18"
29 | },
30 | "devDependencies": {
31 | "@types/jest": "28.1.3",
32 | "@types/react": "18.0.14",
33 | "@types/react-dom": "18.0.5",
34 | "@types/ua-parser-js": "0.7.36",
35 | "husky": "8.0.1",
36 | "prettier": "2.7.1",
37 | "prettier-package-json": "2.6.4",
38 | "pretty-quick": "3.1.3",
39 | "react": "18.2.0",
40 | "react-dom": "18.2.0",
41 | "tsdx": "0.14.1",
42 | "tslib": "2.4.0",
43 | "typescript": "4.7.4"
44 | },
45 | "keywords": [
46 | "browser",
47 | "hooks",
48 | "parser",
49 | "react",
50 | "react-hooks",
51 | "ua",
52 | "ua-parser-js",
53 | "user-agent"
54 | ],
55 | "publishConfig": {
56 | "access": "public"
57 | },
58 | "husky": {
59 | "hooks": {
60 | "pre-commit": "pretty-quick --staged"
61 | }
62 | },
63 | "prettier": {
64 | "printWidth": 80,
65 | "semi": true,
66 | "singleQuote": true,
67 | "trailingComma": "es5"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as UAParser from 'ua-parser-js';
3 |
4 | type IUseUserAgentReturn = Omit;
5 |
6 | function useUserAgent(uastring = window.navigator.userAgent) {
7 | let [state, setState] = React.useState(null);
8 |
9 | React.useEffect(() => {
10 | let didRun = true;
11 |
12 | try {
13 | const uaParser = new UAParser.UAParser();
14 | uaParser.setUA(uastring);
15 | const payload = {
16 | os: uaParser.getOS(),
17 | browser: uaParser.getBrowser(),
18 | cpu: uaParser.getCPU(),
19 | device: uaParser.getDevice(),
20 | engine: uaParser.getEngine(),
21 | };
22 | if (didRun) {
23 | setState(payload);
24 | }
25 | } catch (err) {
26 | if (didRun) {
27 | setState(null);
28 | }
29 | }
30 |
31 | return () => {
32 | didRun = false;
33 | };
34 | }, [uastring]);
35 |
36 | return state;
37 | }
38 |
39 | export { useUserAgent };
40 |
--------------------------------------------------------------------------------
/test/index.test.tsx:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | globalThis.IS_REACT_ACT_ENVIRONMENT = true;
3 |
4 | import * as React from 'react';
5 | import * as ReactDOMClient from 'react-dom/client';
6 | import * as ReactDOMTestUtils from 'react-dom/test-utils';
7 |
8 | import { useUserAgent } from '../src';
9 |
10 | const TestHook = ({ callback }: { callback: any }) => {
11 | callback();
12 | return null;
13 | };
14 |
15 | let root: any = null;
16 | let container: any = null;
17 | beforeEach(() => {
18 | container = document.createElement('div');
19 | document.body.appendChild(container);
20 | });
21 |
22 | afterEach(() => {
23 | ReactDOMTestUtils.act(() => {
24 | root.unmount();
25 | });
26 | container = null;
27 | });
28 |
29 | test('fulfill `details` object correctly', async () => {
30 | let actual: any = null;
31 | ReactDOMTestUtils.act(() => {
32 | root = ReactDOMClient.createRoot(container);
33 | });
34 | ReactDOMTestUtils.act(() => {
35 | root.render(
36 | {
38 | actual = useUserAgent(window.navigator.userAgent);
39 | }}
40 | />
41 | );
42 | });
43 |
44 | let keys = 'os,browser,cpu,device,engine';
45 | expect(Object.keys(actual).join(',')).toBe(keys);
46 | });
47 |
48 | test('uses default value as `window.navigator.userAgent`', async () => {
49 | let actual: any = null;
50 |
51 | ReactDOMTestUtils.act(() => {
52 | root = ReactDOMClient.createRoot(container);
53 | root.render(
54 | {
56 | actual = useUserAgent();
57 | }}
58 | />
59 | );
60 | });
61 |
62 | expect(actual.browser.name).toBe('WebKit');
63 | });
64 |
65 | test('custom user-agent string is parsed correctly', async () => {
66 | let actual: any = null;
67 |
68 | ReactDOMTestUtils.act(() => {
69 | root = ReactDOMClient.createRoot(container);
70 | root.render(
71 | {
73 | actual = useUserAgent(
74 | 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.11 (KHTML, like Gecko) Version/7.1.0.7 Safari/534.11'
75 | );
76 | }}
77 | />
78 | );
79 | });
80 |
81 | expect(actual.browser.name).toBe('Safari');
82 | });
83 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src", "types"],
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "esnext",
6 | "lib": ["dom", "esnext"],
7 | "importHelpers": true,
8 | "declaration": true,
9 | "sourceMap": true,
10 | "strict": true,
11 | "noImplicitAny": true,
12 | "strictNullChecks": true,
13 | "strictFunctionTypes": true,
14 | "strictPropertyInitialization": true,
15 | "noImplicitThis": true,
16 | "alwaysStrict": true,
17 | "noUnusedLocals": true,
18 | "noUnusedParameters": true,
19 | "noImplicitReturns": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "moduleResolution": "node",
22 | "baseUrl": "./",
23 | "paths": {
24 | "*": ["src/*", "node_modules/*"]
25 | },
26 | "jsx": "react",
27 | "esModuleInterop": true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------